[FEATURE] Add trait to detect public deprecated methods 56/57156/8
authorBenni Mack <benni@typo3.org>
Sat, 9 Jun 2018 13:24:26 +0000 (15:24 +0200)
committerChristian Kuhn <lolli@schwarzbu.ch>
Thu, 14 Jun 2018 07:49:02 +0000 (09:49 +0200)
The patch adds a new trait similar to the existing one for
propreties to allow setting public methods to protected in
a backwards compatible way.

Resolves: #85247
Related: #81330
Releases: master
Change-Id: Iaf6d7f328f7b5685e179f4a4b8f78fbeb419fb14
Reviewed-on: https://review.typo3.org/57156
Tested-by: TYPO3com <no-reply@typo3.com>
Reviewed-by: Mathias Brodala <mbrodala@pagemachine.de>
Tested-by: Mathias Brodala <mbrodala@pagemachine.de>
Reviewed-by: Christian Kuhn <lolli@schwarzbu.ch>
Tested-by: Christian Kuhn <lolli@schwarzbu.ch>
typo3/sysext/core/Classes/Compatibility/PublicMethodDeprecationTrait.php [new file with mode: 0644]
typo3/sysext/core/Documentation/Changelog/master/Feature-85247-TraitToDetectPublicDeprecatedMethods.rst [new file with mode: 0644]
typo3/sysext/core/Tests/UnitDeprecated/Compatibility/Fixtures/PublicMethodDeprecationTraitTextFixture.php [new file with mode: 0644]
typo3/sysext/core/Tests/UnitDeprecated/Compatibility/PublicMethodDeprecationTraitTest.php [new file with mode: 0644]
typo3/sysext/core/Tests/UnitDeprecated/Compatibility/PublicPropertyDeprecationTraitTest.php

diff --git a/typo3/sysext/core/Classes/Compatibility/PublicMethodDeprecationTrait.php b/typo3/sysext/core/Classes/Compatibility/PublicMethodDeprecationTrait.php
new file mode 100644 (file)
index 0000000..a1328c6
--- /dev/null
@@ -0,0 +1,97 @@
+<?php
+declare(strict_types = 1);
+namespace TYPO3\CMS\Core\Compatibility;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+/**
+ * Trait to support the logging of deprecation of public INSTANCE methods.
+ *
+ * This is useful due to the long list of PHP4 methods that have been set to
+ * public previously, which should be removed or moved to "protected" / "private".
+ *
+ * Usage:
+ *
+ * 1. Use this trait for the class with the method to change the visibility status or to be removed.
+ * 2. Add the class methods with deprecated public visibility to $deprecatedPublicMethods.
+ * 3. Set these methods to protected or private.
+ * 4. Add the phpDoc tag "@private" to the method so IDEs understand that.
+ *
+ * With the next major release remove the "@private" tag and remove the methods from
+ * $deprecatedPublicMethods. Remove the trait use after removing the last deprecation.
+ *
+ * Note:
+ *
+ * - Only use this trait in classes only that do not define their own magic __call() method.
+ * - Do not use this trait for static methods.
+ *
+ * Example usage:
+ *
+ *
+ * class MyControllerClass {
+ *     use PublicMethodDeprecationTrait;
+ *
+ *     /**
+ *       * List previously publicly accessible variables
+ *       * @var array
+ *       *...
+ *     private $deprecatedPublicMethods = [
+ *         'myMethod' => 'Using MyControllerClass::myMethod() is deprecated and will not be possible anymore in TYPO3 v10. Use MyControllerClass:myOtherMethod() instead.'
+ *     ];
+ *
+ *     /**
+ *      * This is my method.
+ *      *
+ *      * @deprecated (if deprecated)
+ *      * @private (if switched to private)
+ *      /
+ *     protected function myMethod($arg1, $arg2);
+ * }
+ */
+
+/**
+ * This trait has no public methods by default, ensure to add a $deprecatedPublicMethods property
+ * to your class when using this trait.
+ */
+trait PublicMethodDeprecationTrait
+{
+    /**
+     * Checks if the method of the given name is available, calls it but throws a deprecation.
+     * If the method does not exist, a fatal error is thrown.
+     *
+     * Unavailable protected methods must return in a fatal error as usual.
+     * Marked methods are called and a deprecation entry is thrown.
+     *
+     * __call() is not called for public methods.
+     *
+     * @property array $deprecatedPublicMethods List of deprecated public methods
+     * @param string $methodName
+     * @param array $arguments
+     * @return mixed
+     */
+    public function __call(string $methodName, array $arguments)
+    {
+        if (method_exists($this, $methodName) && isset($this->deprecatedPublicMethods[$methodName])) {
+            trigger_error($this->deprecatedPublicMethods[$methodName], E_USER_DEPRECATED);
+            return $this->$methodName(...$arguments);
+        }
+
+        // Do the same behaviour as calling $myObject->method();
+        if (method_exists($this, $methodName)) {
+            throw new \Error('Call to protected/private method ' . self::class . '::' . $methodName . '()');
+        }
+
+        throw new \Error('Call to undefined method ' . self::class . '::' . $methodName . '()');
+    }
+}
diff --git a/typo3/sysext/core/Documentation/Changelog/master/Feature-85247-TraitToDetectPublicDeprecatedMethods.rst b/typo3/sysext/core/Documentation/Changelog/master/Feature-85247-TraitToDetectPublicDeprecatedMethods.rst
new file mode 100644 (file)
index 0000000..0b8846c
--- /dev/null
@@ -0,0 +1,29 @@
+.. include:: ../../Includes.txt
+
+===========================================================
+Feature: #85247 - Trait to detect public deprecated methods
+===========================================================
+
+See :issue:`85247`
+
+Description
+===========
+
+The trait :php:`TYPO3\CMS\Core\Compatibility\PublicMethodDeprecationTrait` has been added
+to allow setting public methods to protected in a backwards compatible way.
+
+The core uses this trait to set public methods that to should be protected or private but
+are accessible codewise for historical reasons, while extensions using the methods do not
+break, but a deprecation note is logged.
+
+Classes using this trait have a property :php:`$deprecatedPublicMethods` that lists all
+methods covered by the trait.
+
+
+Impact
+======
+
+Core classes using this trait log deprecation notes if an extension uses a method that
+has been made protected using the trait functionality.
+
+.. index:: PHP-API
\ No newline at end of file
diff --git a/typo3/sysext/core/Tests/UnitDeprecated/Compatibility/Fixtures/PublicMethodDeprecationTraitTextFixture.php b/typo3/sysext/core/Tests/UnitDeprecated/Compatibility/Fixtures/PublicMethodDeprecationTraitTextFixture.php
new file mode 100644 (file)
index 0000000..5b8baac
--- /dev/null
@@ -0,0 +1,70 @@
+<?php
+declare(strict_types = 1);
+namespace TYPO3\CMS\Core\Tests\UnitDeprecated\Compatibility\Fixtures;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use TYPO3\CMS\Core\Compatibility\PublicMethodDeprecationTrait;
+
+/**
+ * Test class using public method deprecation trait test fixture
+ */
+class PublicMethodDeprecationTraitTextFixture
+{
+    use PublicMethodDeprecationTrait;
+
+    protected $deprecatedPublicMethods = [
+        'methodMadeProtected' => 'Deprecation text',
+        'methodMadeProtectedWithArguments' => 'Deprecation text',
+        'methodMadeProtectedWithReturn' => 'Deprecation text',
+    ];
+
+    public function standardPublicMethod(): void
+    {
+        throw new \RuntimeException('test', 1528822131);
+    }
+
+    protected function standardProtectedMethod(): void
+    {
+        // empty
+    }
+
+    /**
+     * @private
+     */
+    protected function methodMadeProtected(): void
+    {
+        throw new \RuntimeException('test', 1528822485);
+    }
+
+    /**
+     * @private
+     */
+    protected function methodMadeProtectedWithReturn(): string
+    {
+        return 'foo';
+    }
+
+    /**
+     * @private
+     * @param string $arg1
+     * @param string $arg2
+     */
+    protected function methodMadeProtectedWithArguments(string $arg1, string $arg2): void
+    {
+        if ($arg1 === 'foo' && $arg2 === 'bar') {
+            throw new \RuntimeException('test', 1528822486);
+        }
+    }
+}
diff --git a/typo3/sysext/core/Tests/UnitDeprecated/Compatibility/PublicMethodDeprecationTraitTest.php b/typo3/sysext/core/Tests/UnitDeprecated/Compatibility/PublicMethodDeprecationTraitTest.php
new file mode 100644 (file)
index 0000000..7c04a9f
--- /dev/null
@@ -0,0 +1,81 @@
+<?php
+declare(strict_types = 1);
+namespace TYPO3\CMS\Core\Tests\UnitDeprecated\Compatibility;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use TYPO3\CMS\Core\Tests\UnitDeprecated\Compatibility\Fixtures\PublicMethodDeprecationTraitTextFixture;
+use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
+
+/**
+ * Test case
+ */
+class PublicMethodDeprecationTraitTest extends UnitTestCase
+{
+    /**
+     * @test
+     */
+    public function publicMethodCanBeCalled(): void
+    {
+        $this->expectException(\RuntimeException::class);
+        $this->expectExceptionCode(1528822131);
+        (new PublicMethodDeprecationTraitTextFixture())->standardPublicMethod();
+    }
+
+    /**
+     * @test
+     */
+    public function protectedMethodNotHandledByTraitThrowsError(): void
+    {
+        $this->expectException(\Error::class);
+        (new PublicMethodDeprecationTraitTextFixture())->standardProtectedMethod();
+    }
+
+    /**
+     * @test
+     */
+    public function notExistingMethodThrowsError(): void
+    {
+        $this->expectException(\Error::class);
+        (new PublicMethodDeprecationTraitTextFixture())->doesNotExist();
+    }
+
+    /**
+     * @test
+     */
+    public function methodMadeProtectedCanBeCalled(): void
+    {
+        $this->expectException(\RuntimeException::class);
+        $this->expectExceptionCode(1528822485);
+        (new PublicMethodDeprecationTraitTextFixture())->methodMadeProtected();
+    }
+
+    /**
+     * @test
+     */
+    public function methodMadeProtectedReturnsValue(): void
+    {
+        $this->assertEquals('foo', (new PublicMethodDeprecationTraitTextFixture())->methodMadeProtectedWithReturn());
+    }
+
+    /**
+     * @test
+     */
+    public function methodMadeProtectedCanBeCalledWithArguments(): void
+    {
+        $this->expectException(\RuntimeException::class);
+        $this->expectExceptionCode(1528822486);
+        (new PublicMethodDeprecationTraitTextFixture())->methodMadeProtectedWithArguments('foo', 'bar');
+    }
+}
index 04e3026..624db22 100644 (file)
@@ -18,6 +18,9 @@ namespace TYPO3\CMS\Core\Tests\Unit\Compatibility;
 use TYPO3\CMS\Core\Compatibility\PublicPropertyDeprecationTrait;
 use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
 
+/**
+ * Test case
+ */
 class PublicPropertyDeprecationTraitTest extends UnitTestCase
 {
     /**