diff --git a/Classes/Dashboard/Provider/NewestPageviews.php b/Classes/Dashboard/Provider/NewestPageviews.php index 30d3821..9c8ad5f 100644 --- a/Classes/Dashboard/Provider/NewestPageviews.php +++ b/Classes/Dashboard/Provider/NewestPageviews.php @@ -97,6 +97,10 @@ class NewestPageviews implements ListDataProviderInterface $items = $this->queryBuilder->execute()->fetchAll(); foreach ($items as $item) { + if (is_array($item) === false) { + continue; + } + $preparedItems[] = sprintf( '%s - %s', $item['url'], diff --git a/Classes/Dashboard/Provider/PageviewsPerOperatingSystem.php b/Classes/Dashboard/Provider/PageviewsPerOperatingSystem.php index 9c6c6d6..91bb5f6 100644 --- a/Classes/Dashboard/Provider/PageviewsPerOperatingSystem.php +++ b/Classes/Dashboard/Provider/PageviewsPerOperatingSystem.php @@ -114,6 +114,10 @@ class PageviewsPerOperatingSystem implements ChartDataProviderInterface ->fetchAll(); foreach ($result as $row) { + if (is_array($row) === false) { + continue; + } + $labels[] = mb_strimwidth($row['operating_system'], 0, 50, '…'); $data[] = $row['total']; } diff --git a/Classes/Dashboard/Provider/PageviewsPerPage.php b/Classes/Dashboard/Provider/PageviewsPerPage.php index aa8377d..9ac47bc 100644 --- a/Classes/Dashboard/Provider/PageviewsPerPage.php +++ b/Classes/Dashboard/Provider/PageviewsPerPage.php @@ -140,6 +140,10 @@ class PageviewsPerPage implements ChartDataProviderInterface ->fetchAll(); foreach ($result as $row) { + if (is_array($row) === false) { + continue; + } + $labels[] = $this->getRecordTitle($row['pid']); $data[] = $row['total']; } @@ -157,6 +161,10 @@ class PageviewsPerPage implements ChartDataProviderInterface $record = $this->pageRepository->getRecordOverlay('pages', $record, $this->languageLimitation[0]); } + if (is_array($record) === false) { + return 'Unkown'; + } + return strip_tags(BackendUtility::getRecordTitle('pages', $record, true)); } } diff --git a/Classes/Dashboard/Provider/Recordviews.php b/Classes/Dashboard/Provider/Recordviews.php index 92af663..9aed165 100644 --- a/Classes/Dashboard/Provider/Recordviews.php +++ b/Classes/Dashboard/Provider/Recordviews.php @@ -26,7 +26,6 @@ use Doctrine\DBAL\ParameterType; use Doctrine\DBAL\Statement; use TYPO3\CMS\Backend\Utility\BackendUtility; use TYPO3\CMS\Core\Database\Connection; -use TYPO3\CMS\Core\Database\ConnectionPool; use TYPO3\CMS\Core\Database\Query\QueryBuilder; use TYPO3\CMS\Core\Database\Query\Restriction\EndTimeRestriction; use TYPO3\CMS\Core\Database\Query\Restriction\HiddenRestriction; @@ -37,11 +36,6 @@ use TYPO3\CMS\Dashboard\Widgets\ChartDataProviderInterface; class Recordviews implements ChartDataProviderInterface { - /** - * @var ConnectionPool - */ - private $connectionPool; - /** * @var PageRepository */ @@ -83,7 +77,6 @@ class Recordviews implements ChartDataProviderInterface private $recordTypeLimitation; public function __construct( - ConnectionPool $connectionPool, PageRepository $pageRepository, QueryBuilder $queryBuilder, int $days = 31, @@ -93,7 +86,6 @@ class Recordviews implements ChartDataProviderInterface array $recordTableLimitation = [], array $recordTypeLimitation = [] ) { - $this->connectionPool = $connectionPool; $this->pageRepository = $pageRepository; $this->queryBuilder = $queryBuilder; $this->days = $days; @@ -131,8 +123,11 @@ class Recordviews implements ChartDataProviderInterface ); if ( - $this->recordTypeLimitation !== [] - && in_array($record['type'], $this->recordTypeLimitation) === false + $record === null + || ( + $this->recordTypeLimitation !== [] + && in_array($record['type'], $this->recordTypeLimitation) === false + ) ) { continue; } @@ -208,7 +203,7 @@ class Recordviews implements ChartDataProviderInterface private function getRecord( int $uid, string $table - ): array { + ): ?array { $recordTypeField = $GLOBALS['TCA'][$table]['ctrl']['type'] ?? ''; $record = BackendUtility::getRecord($table, $uid); @@ -216,6 +211,10 @@ class Recordviews implements ChartDataProviderInterface $record = $this->pageRepository->getRecordOverlay($table, $record, $this->languageLimitation[0]); } + if (is_array($record) === false) { + return null; + } + return [ 'title' => strip_tags(BackendUtility::getRecordTitle($table, $record, true)), 'type' => $record[$recordTypeField] ?? '', diff --git a/Classes/Domain/Model/RecordRule.php b/Classes/Domain/Model/RecordRule.php index ee05b77..32ebf69 100644 --- a/Classes/Domain/Model/RecordRule.php +++ b/Classes/Domain/Model/RecordRule.php @@ -23,11 +23,6 @@ namespace DanielSiepmann\Tracking\Domain\Model; class RecordRule { - /** - * @var string - */ - private $identifier; - /** * @var string */ @@ -44,12 +39,10 @@ class RecordRule private $tableName; public function __construct( - string $identifier, string $matches, string $recordUid, string $tableName ) { - $this->identifier = $identifier; $this->matches = $matches; $this->recordUid = $recordUid; $this->tableName = $tableName; @@ -58,7 +51,6 @@ class RecordRule public static function fromArray(array $config): self { return new RecordRule( - $config['identifier'], $config['matches'], $config['recordUid'], $config['tableName'] @@ -68,13 +60,8 @@ class RecordRule public static function multipleFromArray(array $configs): array { $rules = []; - foreach ($configs as $identifier => $config) { - $rules[] = static::fromArray(array_merge( - [ - 'identifier' => $identifier, - ], - $config - )); + foreach ($configs as $config) { + $rules[] = static::fromArray($config); } return $rules; } diff --git a/Classes/Domain/Pageview/Factory.php b/Classes/Domain/Pageview/Factory.php index 10726eb..b8687ce 100644 --- a/Classes/Domain/Pageview/Factory.php +++ b/Classes/Domain/Pageview/Factory.php @@ -24,6 +24,7 @@ namespace DanielSiepmann\Tracking\Domain\Pageview; use DanielSiepmann\Tracking\Domain\Model\Pageview; use Psr\Http\Message\ServerRequestInterface; use TYPO3\CMS\Core\Routing\PageArguments; +use TYPO3\CMS\Core\Site\Entity\SiteLanguage; use TYPO3\CMS\Core\Site\SiteFinder; class Factory @@ -41,10 +42,10 @@ class Factory public static function fromRequest(ServerRequestInterface $request): Pageview { return new Pageview( - static::getRouting($request)->getPageId(), - $request->getAttribute('language'), + self::getRouting($request)->getPageId(), + self::getLanguage($request), new \DateTimeImmutable(), - (int) static::getRouting($request)->getPageType(), + (int) self::getRouting($request)->getPageType(), (string) $request->getUri(), $request->getHeader('User-Agent')[0] ?? '' ); @@ -63,8 +64,25 @@ class Factory ); } + private static function getLanguage(ServerRequestInterface $request): SiteLanguage + { + $language = $request->getAttribute('language'); + + if (!$language instanceof SiteLanguage) { + throw new \UnexpectedValueException('Could not fetch SiteLanguage from request attributes.', 1637847002); + } + + return $language; + } + private static function getRouting(ServerRequestInterface $request): PageArguments { - return $request->getAttribute('routing'); + $routing = $request->getAttribute('routing'); + + if (!$routing instanceof PageArguments) { + throw new \UnexpectedValueException('Could not fetch PageArguments from request attributes.', 1637847002); + } + + return $routing; } } diff --git a/Classes/Domain/Recordview/Factory.php b/Classes/Domain/Recordview/Factory.php index 32c68eb..f4761b5 100644 --- a/Classes/Domain/Recordview/Factory.php +++ b/Classes/Domain/Recordview/Factory.php @@ -26,6 +26,7 @@ use DanielSiepmann\Tracking\Domain\Model\Recordview; use Psr\Http\Message\ServerRequestInterface; use Symfony\Component\ExpressionLanguage\ExpressionLanguage; use TYPO3\CMS\Core\Routing\PageArguments; +use TYPO3\CMS\Core\Site\Entity\SiteLanguage; class Factory { @@ -39,19 +40,47 @@ class Factory ['request' => $request] ); + if (is_numeric($recordUid) === false) { + throw new \UnexpectedValueException( + sprintf( + 'Could not determine record uid based on expression: "%1$s", got type "%2$s".', + $rule->getUidExpression(), + gettype($recordUid) + ), + 1637846881 + ); + } + return new Recordview( - static::getRouting($request)->getPageId(), - $request->getAttribute('language'), + self::getRouting($request)->getPageId(), + self::getLanguage($request), new \DateTimeImmutable(), (string) $request->getUri(), $request->getHeader('User-Agent')[0] ?? '', - $recordUid, + (int) $recordUid, $rule->getTableName() ); } + private static function getLanguage(ServerRequestInterface $request): SiteLanguage + { + $language = $request->getAttribute('language'); + + if (!$language instanceof SiteLanguage) { + throw new \UnexpectedValueException('Could not fetch SiteLanguage from request attributes.', 1637847002); + } + + return $language; + } + private static function getRouting(ServerRequestInterface $request): PageArguments { - return $request->getAttribute('routing'); + $routing = $request->getAttribute('routing'); + + if (!$routing instanceof PageArguments) { + throw new \UnexpectedValueException('Could not fetch PageArguments from request attributes.', 1637847002); + } + + return $routing; } } diff --git a/Classes/Domain/Repository/Pageview.php b/Classes/Domain/Repository/Pageview.php index 978fc61..f24df7b 100644 --- a/Classes/Domain/Repository/Pageview.php +++ b/Classes/Domain/Repository/Pageview.php @@ -47,11 +47,17 @@ class Pageview public function countAll(): int { - return $this->connection->createQueryBuilder() + $result = $this->connection->createQueryBuilder() ->count('uid') ->from('tx_tracking_pageview') ->execute() ->fetchColumn(); + + if (is_numeric($result)) { + return (int) $result; + } + + return 0; } public function findAll(): \Generator @@ -60,6 +66,10 @@ class Pageview $pageViews = $queryBuilder->select('*')->from('tx_tracking_pageview')->execute(); while ($pageView = $pageViews->fetch()) { + if (is_array($pageView) === false) { + continue; + } + yield $this->factory->fromDbRow($pageView); } } diff --git a/Tests/Functional/Dashboard/Provider/RecordviewsTest.php b/Tests/Functional/Dashboard/Provider/RecordviewsTest.php index b85777d..79d4dbe 100644 --- a/Tests/Functional/Dashboard/Provider/RecordviewsTest.php +++ b/Tests/Functional/Dashboard/Provider/RecordviewsTest.php @@ -22,7 +22,6 @@ namespace DanielSiepmann\Tracking\Tests\Functional\Dashboard\Provider; */ use DanielSiepmann\Tracking\Dashboard\Provider\Recordviews; -use TYPO3\CMS\Core\Database\ConnectionPool; use TYPO3\CMS\Core\Domain\Repository\PageRepository; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase as TestCase; @@ -52,13 +51,9 @@ class RecordviewsTest extends TestCase ]); } - $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class); - /* @var ConnectionPool $connectionPool */ - $subject = new Recordviews( - $connectionPool, GeneralUtility::makeInstance(PageRepository::class), - $connectionPool->getQueryBuilderForTable('tx_tracking_recordview') + $this->getConnectionPool()->getQueryBuilderForTable('tx_tracking_recordview') ); $result = $subject->getChartData(); @@ -105,13 +100,9 @@ class RecordviewsTest extends TestCase 'record_table_name' => 'sys_category', ]); - $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class); - /* @var ConnectionPool $connectionPool */ - $subject = new Recordviews( - $connectionPool, GeneralUtility::makeInstance(PageRepository::class), - $connectionPool->getQueryBuilderForTable('tx_tracking_recordview'), + $this->getConnectionPool()->getQueryBuilderForTable('tx_tracking_recordview'), 2 ); @@ -150,13 +141,9 @@ class RecordviewsTest extends TestCase 'record_table_name' => 'sys_category', ]); - $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class); - /* @var ConnectionPool $connectionPool */ - $subject = new Recordviews( - $connectionPool, GeneralUtility::makeInstance(PageRepository::class), - $connectionPool->getQueryBuilderForTable('tx_tracking_recordview'), + $this->getConnectionPool()->getQueryBuilderForTable('tx_tracking_recordview'), 2 ); @@ -184,13 +171,9 @@ class RecordviewsTest extends TestCase ]); } - $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class); - /* @var ConnectionPool $connectionPool */ - $subject = new Recordviews( - $connectionPool, GeneralUtility::makeInstance(PageRepository::class), - $connectionPool->getQueryBuilderForTable('tx_tracking_recordview'), + $this->getConnectionPool()->getQueryBuilderForTable('tx_tracking_recordview'), 31, 2 ); @@ -220,13 +203,9 @@ class RecordviewsTest extends TestCase ]); } - $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class); - /* @var ConnectionPool $connectionPool */ - $subject = new Recordviews( - $connectionPool, GeneralUtility::makeInstance(PageRepository::class), - $connectionPool->getQueryBuilderForTable('tx_tracking_recordview'), + $this->getConnectionPool()->getQueryBuilderForTable('tx_tracking_recordview'), 31, 6, [1, 2, 3, 4, 5] @@ -265,13 +244,9 @@ class RecordviewsTest extends TestCase 'record_table_name' => 'tt_content', ]); - $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class); - /* @var ConnectionPool $connectionPool */ - $subject = new Recordviews( - $connectionPool, GeneralUtility::makeInstance(PageRepository::class), - $connectionPool->getQueryBuilderForTable('tx_tracking_recordview'), + $this->getConnectionPool()->getQueryBuilderForTable('tx_tracking_recordview'), 31, 6, [], @@ -308,13 +283,9 @@ class RecordviewsTest extends TestCase ]); } - $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class); - /* @var ConnectionPool $connectionPool */ - $subject = new Recordviews( - $connectionPool, GeneralUtility::makeInstance(PageRepository::class), - $connectionPool->getQueryBuilderForTable('tx_tracking_recordview'), + $this->getConnectionPool()->getQueryBuilderForTable('tx_tracking_recordview'), 31, 6, [], @@ -360,13 +331,9 @@ class RecordviewsTest extends TestCase 'record_table_name' => 'sys_category', ]); - $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class); - /* @var ConnectionPool $connectionPool */ - $subject = new Recordviews( - $connectionPool, GeneralUtility::makeInstance(PageRepository::class), - $connectionPool->getQueryBuilderForTable('tx_tracking_recordview'), + $this->getConnectionPool()->getQueryBuilderForTable('tx_tracking_recordview'), 31, 6, [], @@ -412,13 +379,9 @@ class RecordviewsTest extends TestCase 'record_table_name' => 'sys_category', ]); - $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class); - /* @var ConnectionPool $connectionPool */ - $subject = new Recordviews( - $connectionPool, GeneralUtility::makeInstance(PageRepository::class), - $connectionPool->getQueryBuilderForTable('tx_tracking_recordview'), + $this->getConnectionPool()->getQueryBuilderForTable('tx_tracking_recordview'), 31, 6, [], diff --git a/Tests/Functional/Typo3FeaturesTest.php b/Tests/Functional/Typo3FeaturesTest.php index ab30ae9..95e5864 100644 --- a/Tests/Functional/Typo3FeaturesTest.php +++ b/Tests/Functional/Typo3FeaturesTest.php @@ -36,16 +36,32 @@ class Typo3FeaturesTest extends TestCase 'typo3conf/ext/tracking', ]; + public function setUp(): void + { + parent::setUp(); + + $this->importDataSet('EXT:tracking/Tests/Functional/Fixtures/Typo3FeaturesTest/PageWithRecords.xml'); + $this->setUpBackendUserFromFixture(1); + $languageServiceFactory = $this->getContainer()->get(LanguageServiceFactory::class); + if (!$languageServiceFactory instanceof LanguageServiceFactory) { + throw new \UnexpectedValueException('Did not retrieve LanguageServiceFactory.', 1637847250); + } + $GLOBALS['LANG'] = $languageServiceFactory->create('default'); + } + + public function tearDown(): void + { + unset($GLOBALS['LANG']); + + parent::tearDown(); + } + /** * @test * @testdox Copy pages. Tracking records will not be copied. */ public function copyContainingRecords(): void { - $this->importDataSet('EXT:tracking/Tests/Functional/Fixtures/Typo3FeaturesTest/PageWithRecords.xml'); - $this->setUpBackendUserFromFixture(1); - $GLOBALS['LANG'] = $this->getContainer()->get(LanguageServiceFactory::class)->create('default'); - $dataHandler = new DataHandler(); $dataHandler->start([], [ 'pages' => [ @@ -68,10 +84,6 @@ class Typo3FeaturesTest extends TestCase */ public function copyCustomTablesViaDataHandler(): void { - $this->importDataSet('EXT:tracking/Tests/Functional/Fixtures/Typo3FeaturesTest/PageWithRecords.xml'); - $this->setUpBackendUserFromFixture(1); - $GLOBALS['LANG'] = $this->getContainer()->get(LanguageServiceFactory::class)->create('default'); - $dataHandler = new DataHandler(); $dataHandler->copyWhichTables = 'pages,tx_tracking_pageview,tx_tracking_recordview'; $dataHandler->start([], [ diff --git a/Tests/Unit/Domain/Model/RecordRuleTest.php b/Tests/Unit/Domain/Model/RecordRuleTest.php index c3f01b9..e61a5e3 100644 --- a/Tests/Unit/Domain/Model/RecordRuleTest.php +++ b/Tests/Unit/Domain/Model/RecordRuleTest.php @@ -35,7 +35,6 @@ class RecordRuleTest extends TestCase public function canBeCreatedViaConstructor(): void { $subject = new RecordRule( - '', '', '', '' @@ -88,7 +87,6 @@ class RecordRuleTest extends TestCase public function returnsMatchExpression(): void { $subject = new RecordRule( - '', 'match expression', '', '' @@ -103,7 +101,6 @@ class RecordRuleTest extends TestCase public function returnsUidExpression(): void { $subject = new RecordRule( - '', '', 'match expression', '' @@ -118,7 +115,6 @@ class RecordRuleTest extends TestCase public function returnsTableName(): void { $subject = new RecordRule( - '', '', '', 'table_name' diff --git a/Tests/Unit/Domain/Recordview/FactoryTest.php b/Tests/Unit/Domain/Recordview/FactoryTest.php index 973ce63..50efccb 100644 --- a/Tests/Unit/Domain/Recordview/FactoryTest.php +++ b/Tests/Unit/Domain/Recordview/FactoryTest.php @@ -43,7 +43,7 @@ class FactoryTest extends TestCase public function returnsRecordviewFromRequest(): void { $rule = $this->prophesize(RecordRule::class); - $rule->getUidExpression()->willReturn('request.getQueryParams()["category"] > 0'); + $rule->getUidExpression()->willReturn('request.getQueryParams()["category"]'); $rule->getTableName()->willReturn('sys_category'); $routing = $this->prophesize(PageArguments::class); @@ -70,7 +70,7 @@ class FactoryTest extends TestCase public function returnedRecordviewContainsUserAgent(): void { $rule = $this->prophesize(RecordRule::class); - $rule->getUidExpression()->willReturn('request.getQueryParams()["category"] > 0'); + $rule->getUidExpression()->willReturn('request.getQueryParams()["category"]'); $rule->getTableName()->willReturn('sys_category'); $routing = $this->prophesize(PageArguments::class); @@ -97,7 +97,7 @@ class FactoryTest extends TestCase public function returnedRecordviewContainsUri(): void { $rule = $this->prophesize(RecordRule::class); - $rule->getUidExpression()->willReturn('request.getQueryParams()["category"] > 0'); + $rule->getUidExpression()->willReturn('request.getQueryParams()["category"]'); $rule->getTableName()->willReturn('sys_category'); $routing = $this->prophesize(PageArguments::class); @@ -124,7 +124,7 @@ class FactoryTest extends TestCase public function returnedRecordviewContainsDateTime(): void { $rule = $this->prophesize(RecordRule::class); - $rule->getUidExpression()->willReturn('request.getQueryParams()["category"] > 0'); + $rule->getUidExpression()->willReturn('request.getQueryParams()["category"]'); $rule->getTableName()->willReturn('sys_category'); $routing = $this->prophesize(PageArguments::class); @@ -151,7 +151,7 @@ class FactoryTest extends TestCase public function returnedRecordviewContainsLanguage(): void { $rule = $this->prophesize(RecordRule::class); - $rule->getUidExpression()->willReturn('request.getQueryParams()["category"] > 0'); + $rule->getUidExpression()->willReturn('request.getQueryParams()["category"]'); $rule->getTableName()->willReturn('sys_category'); $routing = $this->prophesize(PageArguments::class); @@ -178,7 +178,7 @@ class FactoryTest extends TestCase public function returnedRecordviewContainsPageId(): void { $rule = $this->prophesize(RecordRule::class); - $rule->getUidExpression()->willReturn('request.getQueryParams()["category"] > 0'); + $rule->getUidExpression()->willReturn('request.getQueryParams()["category"]'); $rule->getTableName()->willReturn('sys_category'); $routing = $this->prophesize(PageArguments::class); @@ -232,7 +232,7 @@ class FactoryTest extends TestCase public function returnedRecordviewContainsTableName(): void { $rule = $this->prophesize(RecordRule::class); - $rule->getUidExpression()->willReturn('request.getQueryParams()["category"] > 0'); + $rule->getUidExpression()->willReturn('request.getQueryParams()["category"]'); $rule->getTableName()->willReturn('sys_category'); $routing = $this->prophesize(PageArguments::class); diff --git a/composer.json b/composer.json index a0fa003..9910d27 100644 --- a/composer.json +++ b/composer.json @@ -40,12 +40,12 @@ }, "require-dev": { "phpunit/phpunit": "^9.0", - "phpstan/phpstan": "^0.12.18", - "phpstan/extension-installer": "^1.0", - "jangregor/phpstan-prophecy": "^0.8.1", + "phpstan/phpstan": "^1.2", + "phpstan/extension-installer": "^1.1", + "jangregor/phpstan-prophecy": "^1.0", "phpspec/prophecy-phpunit": "^2.0", "typo3/testing-framework": "^6.14", - "saschaegerer/phpstan-typo3": "^0.13.1", + "saschaegerer/phpstan-typo3": "^1.0", "symplify/easy-coding-standard": "^9.3", "cweagans/composer-patches": "^1.7" },