From 4ff50d151a4d402a1dcabe5deaa7a4ef8c88b2ee Mon Sep 17 00:00:00 2001
From: Henrik Elsner <helsner@dfau.de>
Date: Sat, 6 Nov 2021 22:15:07 +0100
Subject: [PATCH] [FEATURE] Handle ->getPublicUrl() on FAL and MediaHelper
 objects

Resolves #2591
---
 config/v11/typo3-113.php                      |  2 +
 ...dlePublicFALUrlsWithRelativePathRector.php | 83 +++++++++++++++++++
 .../GeneratePublicUrlForResourceEvent.php     | 15 ++++
 .../Helpers/OnlineMediaHelperInterface.php    | 14 ++++
 .../Fixture/fixture.php.inc                   | 70 ++++++++++++++++
 ...ublicFALUrlsWithRelativePathRectorTest.php | 32 +++++++
 .../config/configured_rule.php                | 13 +++
 7 files changed, 229 insertions(+)
 create mode 100644 rules/TYPO311/v3/HandlePublicFALUrlsWithRelativePathRector.php
 create mode 100644 stubs/TYPO3/CMS/Core/Resource/Event/GeneratePublicUrlForResourceEvent.php
 create mode 100644 stubs/TYPO3/CMS/Core/Resource/OnlineMedia/Helpers/OnlineMediaHelperInterface.php
 create mode 100644 tests/Rector/v11/v3/HandlePublicFALUrlsWithRelativePathRector/Fixture/fixture.php.inc
 create mode 100644 tests/Rector/v11/v3/HandlePublicFALUrlsWithRelativePathRector/HandlePublicFALUrlsWithRelativePathRectorTest.php
 create mode 100644 tests/Rector/v11/v3/HandlePublicFALUrlsWithRelativePathRector/config/configured_rule.php

diff --git a/config/v11/typo3-113.php b/config/v11/typo3-113.php
index 24d133b22..25fe93f9c 100644
--- a/config/v11/typo3-113.php
+++ b/config/v11/typo3-113.php
@@ -3,6 +3,7 @@
 declare(strict_types=1);
 
 use Rector\Config\RectorConfig;
+use Ssch\TYPO3Rector\TYPO311\v3\HandlePublicFALUrlsWithRelativePathRector;
 use Ssch\TYPO3Rector\TYPO311\v3\MigrateHttpUtilityRedirectRector;
 use Ssch\TYPO3Rector\TYPO311\v3\SubstituteExtbaseRequestGetBaseUriRector;
 use Ssch\TYPO3Rector\TYPO311\v3\SubstituteMethodRmFromListOfGeneralUtilityRector;
@@ -16,4 +17,5 @@
     $rectorConfig->rule(SubstituteExtbaseRequestGetBaseUriRector::class);
     $rectorConfig->rule(UseNormalizedParamsToGetRequestUrlRector::class);
     $rectorConfig->rule(MigrateHttpUtilityRedirectRector::class);
+    $rectorConfig->rule(HandlePublicFALUrlsWithRelativePathRector::class);
 };
diff --git a/rules/TYPO311/v3/HandlePublicFALUrlsWithRelativePathRector.php b/rules/TYPO311/v3/HandlePublicFALUrlsWithRelativePathRector.php
new file mode 100644
index 000000000..9882e4135
--- /dev/null
+++ b/rules/TYPO311/v3/HandlePublicFALUrlsWithRelativePathRector.php
@@ -0,0 +1,83 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ssch\TYPO3Rector\TYPO311\v3;
+
+use PhpParser\Node;
+use PhpParser\Node\Expr\MethodCall;
+use PHPStan\Type\ObjectType;
+use Rector\Rector\AbstractRector;
+use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
+use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
+
+/**
+ * @changelog https://docs.typo3.org/c/typo3/cms-core/main/en-us/Changelog/11.3/Deprecation-94193-PublicUrlWithRelativePathsInFALAPI.html
+ * @see \Ssch\TYPO3Rector\Tests\Rector\v11\v3\HandlePublicFALUrlsWithRelativePathRector\HandlePublicFALUrlsWithRelativePathRectorTest
+ */
+final class HandlePublicFALUrlsWithRelativePathRector extends AbstractRector
+{
+    /**
+     * @return array<class-string<Node>>
+     */
+    public function getNodeTypes(): array
+    {
+        return [MethodCall::class];
+    }
+
+    /**
+     * @param Node\Expr\MethodCall $node
+     */
+    public function refactor(Node $node): ?Node
+    {
+        if ($this->shouldSkip($node)) {
+            return null;
+        }
+
+        if ($this->nodeTypeResolver->isMethodStaticCallOrClassMethodObjectType(
+            $node,
+            new ObjectType('TYPO3\CMS\Core\Resource\FileInterface')
+        )) {
+            $node->args = [];
+        }
+
+        if ($this->nodeTypeResolver->isMethodStaticCallOrClassMethodObjectType(
+            $node,
+            new ObjectType('TYPO3\CMS\Core\Resource\OnlineMedia\Helpers\OnlineMediaHelperInterface')
+        )) {
+            $node->args = [$node->args[0]];
+        }
+
+        return $node;
+    }
+
+    /**
+     * @codeCoverageIgnore
+     */
+    public function getRuleDefinition(): RuleDefinition
+    {
+        return new RuleDefinition('Handles public FAL urls with relative paths', [new CodeSample(
+            <<<'CODE_SAMPLE'
+CODE_SAMPLE
+            ,
+            <<<'CODE_SAMPLE'
+CODE_SAMPLE
+        )]);
+    }
+
+    private function shouldSkip(MethodCall $node): bool
+    {
+        if (! $this->nodeTypeResolver->isMethodStaticCallOrClassMethodObjectType(
+            $node,
+            new ObjectType('TYPO3\CMS\Core\Resource\FileInterface')
+        )
+            && ! $this->nodeTypeResolver->isMethodStaticCallOrClassMethodObjectType(
+                $node,
+                new ObjectType('TYPO3\CMS\Core\Resource\OnlineMedia\Helpers\OnlineMediaHelperInterface')
+            )) {
+            return true;
+        }
+
+        return ! $this->isName($node->name, 'getPublicUrl');
+    }
+}
diff --git a/stubs/TYPO3/CMS/Core/Resource/Event/GeneratePublicUrlForResourceEvent.php b/stubs/TYPO3/CMS/Core/Resource/Event/GeneratePublicUrlForResourceEvent.php
new file mode 100644
index 000000000..be009721b
--- /dev/null
+++ b/stubs/TYPO3/CMS/Core/Resource/Event/GeneratePublicUrlForResourceEvent.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace TYPO3\CMS\Core\Resource;
+
+if (class_exists('TYPO3\CMS\Core\Resource\Event\GeneratePublicUrlForResourceEvent')) {
+    return;
+}
+
+class GeneratePublicUrlForResourceEvent
+{
+    public function isRelativeToCurrentScript(): bool
+    {
+        return true;
+    }
+}
diff --git a/stubs/TYPO3/CMS/Core/Resource/OnlineMedia/Helpers/OnlineMediaHelperInterface.php b/stubs/TYPO3/CMS/Core/Resource/OnlineMedia/Helpers/OnlineMediaHelperInterface.php
new file mode 100644
index 000000000..74f06fdf7
--- /dev/null
+++ b/stubs/TYPO3/CMS/Core/Resource/OnlineMedia/Helpers/OnlineMediaHelperInterface.php
@@ -0,0 +1,14 @@
+<?php
+
+namespace TYPO3\CMS\Core\Resource\OnlineMedia\Helpers;
+
+use TYPO3\CMS\Core\Resource\File;
+
+if (interface_exists('TYPO3\CMS\Core\Resource\OnlineMedia\Helpers\OnlineMediaHelperInterface')) {
+    return;
+}
+
+interface OnlineMediaHelperInterface
+{
+    public function getPublicUrl(File $file, $relativeToCurrentScript = false);
+}
diff --git a/tests/Rector/v11/v3/HandlePublicFALUrlsWithRelativePathRector/Fixture/fixture.php.inc b/tests/Rector/v11/v3/HandlePublicFALUrlsWithRelativePathRector/Fixture/fixture.php.inc
new file mode 100644
index 000000000..833ee3416
--- /dev/null
+++ b/tests/Rector/v11/v3/HandlePublicFALUrlsWithRelativePathRector/Fixture/fixture.php.inc
@@ -0,0 +1,70 @@
+<?php
+
+namespace Ssch\TYPO3Rector\Tests\Rector\v11\v3\HandlePublicFALUrlsWithRelativePathRector\Fixture;
+
+use TYPO3\CMS\Core\Resource\File;
+use TYPO3\CMS\Core\Resource\FileInterface;
+use TYPO3\CMS\Core\Resource\OnlineMedia\Helpers\OnlineMediaHelperInterface;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+
+class TestFile implements FileInterface
+{
+    public function getPublicUrl(bool $relativeToCurrentScript)
+    {
+        return 'foo';
+    }
+}
+
+class MyCustomMediaHelper implements OnlineMediaHelperInterface
+{
+    public function getPublicUrl(File $file, $relativeToCurrentScript = false)
+    {
+        return 'foo';
+    }
+}
+
+
+$file = GeneralUtility::makeInstance(TestFile::class);
+$foo = $file->getPublicUrl(true);
+
+$helper = GeneralUtility::makeInstance(MyCustomMediaHelper::class);
+$bar = $helper->getPublicUrl($file, true);
+
+// $event = GeneralUtility::makeInstance()
+
+?>
+-----
+<?php
+
+namespace Ssch\TYPO3Rector\Tests\Rector\v11\v3\HandlePublicFALUrlsWithRelativePathRector\Fixture;
+
+use TYPO3\CMS\Core\Resource\File;
+use TYPO3\CMS\Core\Resource\FileInterface;
+use TYPO3\CMS\Core\Resource\OnlineMedia\Helpers\OnlineMediaHelperInterface;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+
+class TestFile implements FileInterface
+{
+    public function getPublicUrl(bool $relativeToCurrentScript)
+    {
+        return 'foo';
+    }
+}
+
+class MyCustomMediaHelper implements OnlineMediaHelperInterface
+{
+    public function getPublicUrl(File $file, $relativeToCurrentScript = false)
+    {
+        return 'foo';
+    }
+}
+
+$file = GeneralUtility::makeInstance(TestFile::class);
+$foo = $file->getPublicUrl();
+
+$helper = GeneralUtility::makeInstance(MyCustomMediaHelper::class);
+$bar = $helper->getPublicUrl($file);
+
+// $event = GeneralUtility::makeInstance()
+
+?>
diff --git a/tests/Rector/v11/v3/HandlePublicFALUrlsWithRelativePathRector/HandlePublicFALUrlsWithRelativePathRectorTest.php b/tests/Rector/v11/v3/HandlePublicFALUrlsWithRelativePathRector/HandlePublicFALUrlsWithRelativePathRectorTest.php
new file mode 100644
index 000000000..06ffe8e57
--- /dev/null
+++ b/tests/Rector/v11/v3/HandlePublicFALUrlsWithRelativePathRector/HandlePublicFALUrlsWithRelativePathRectorTest.php
@@ -0,0 +1,32 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ssch\TYPO3Rector\Tests\Rector\v11\v3\HandlePublicFALUrlsWithRelativePathRector;
+
+use Iterator;
+use Rector\Testing\PHPUnit\AbstractRectorTestCase;
+
+final class HandlePublicFALUrlsWithRelativePathRectorTest extends AbstractRectorTestCase
+{
+    /**
+     * @dataProvider provideData()
+     */
+    public function test(string $filePath): void
+    {
+        $this->doTestFile($filePath);
+    }
+
+    /**
+     * @return Iterator<array<string>>
+     */
+    public static function provideData(): Iterator
+    {
+        return self::yieldFilesFromDirectory(__DIR__ . '/Fixture');
+    }
+
+    public function provideConfigFilePath(): string
+    {
+        return __DIR__ . '/config/configured_rule.php';
+    }
+}
diff --git a/tests/Rector/v11/v3/HandlePublicFALUrlsWithRelativePathRector/config/configured_rule.php b/tests/Rector/v11/v3/HandlePublicFALUrlsWithRelativePathRector/config/configured_rule.php
new file mode 100644
index 000000000..955870756
--- /dev/null
+++ b/tests/Rector/v11/v3/HandlePublicFALUrlsWithRelativePathRector/config/configured_rule.php
@@ -0,0 +1,13 @@
+<?php
+
+declare(strict_types=1);
+
+use Ssch\TYPO3Rector\TYPO311\v3\HandlePublicFALUrlsWithRelativePathRector;
+use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
+
+return static function (ContainerConfigurator $containerConfigurator): void {
+    $containerConfigurator->import(__DIR__ . '/../../../../../../config/config_test.php');
+
+    $services = $containerConfigurator->services();
+    $services->set(HandlePublicFALUrlsWithRelativePathRector::class);
+};