[!!!][TASK] Doctrine: Migrate various quoting methods 29/49529/6
authorBenni Mack <benni@typo3.org>
Sun, 21 Aug 2016 19:51:06 +0000 (21:51 +0200)
committerGeorg Ringer <georg.ringer@gmail.com>
Mon, 22 Aug 2016 17:56:47 +0000 (19:56 +0200)
Various methods related to quoting have been migrated
to Doctrine DBAL.

Additionally, the method LiveSearch->getQueryString()
has been removed as it serves no additional purpose
anymore.

Resolves: #77587
Releases: master
Change-Id: Ia6c67b8a301d954dbd62966d4d3936a2f2dd3ad4
Reviewed-on: https://review.typo3.org/49529
Reviewed-by: Morton Jonuschat <m.jonuschat@mojocode.de>
Tested-by: Bamboo TYPO3com <info@typo3.com>
Tested-by: Morton Jonuschat <m.jonuschat@mojocode.de>
Reviewed-by: Georg Ringer <georg.ringer@gmail.com>
Tested-by: Georg Ringer <georg.ringer@gmail.com>
typo3/sysext/backend/Classes/Form/Wizard/SuggestWizard.php
typo3/sysext/backend/Classes/Search/LiveSearch/LiveSearch.php
typo3/sysext/core/Classes/Utility/RootlineUtility.php
typo3/sysext/core/Documentation/Changelog/master/Breaking-77587-RemovedLiveSearch-getQueryString.rst [new file with mode: 0644]
typo3/sysext/core/Tests/Unit/Utility/RootlineUtilityTest.php
typo3/sysext/frontend/Classes/ContentObject/Menu/AbstractMenuContentObject.php

index be83b16..9887465 100644 (file)
@@ -17,6 +17,7 @@ namespace TYPO3\CMS\Backend\Form\Wizard;
 use Psr\Http\Message\ResponseInterface;
 use Psr\Http\Message\ServerRequestInterface;
 use TYPO3\CMS\Backend\Utility\BackendUtility;
+use TYPO3\CMS\Core\Database\ConnectionPool;
 use TYPO3\CMS\Core\Imaging\Icon;
 use TYPO3\CMS\Core\Imaging\IconFactory;
 use TYPO3\CMS\Core\Utility\ArrayUtility;
@@ -170,7 +171,10 @@ class SuggestWizard
                         $replacement['###PAGE_TSCONFIG_IDLIST###'] =  implode(',', GeneralUtility::intExplode(',', $fieldTSconfig['PAGE_TSCONFIG_IDLIST']));
                     }
                     if (isset($fieldTSconfig['PAGE_TSCONFIG_STR'])) {
-                        $replacement['###PAGE_TSCONFIG_STR###'] = $GLOBALS['TYPO3_DB']->quoteStr($fieldTSconfig['PAGE_TSCONFIG_STR'], $fieldConfig['foreign_table']);
+                        $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($fieldConfig['foreign_table']);
+                        // nasty hack, but it's currently not possible to just quote anything "inside" the value but not escaping
+                        // the whole field as it is not known where it is used in the WHERE clause
+                        $replacement['###PAGE_TSCONFIG_STR###'] = trim($connection->quote($fieldTSconfig['PAGE_TSCONFIG_STR']), '\'');
                     }
                 }
                 $config['addWhere'] = strtr(' ' . $config['addWhere'], $replacement);
index d29f012..afb1061 100644 (file)
@@ -410,17 +410,6 @@ class LiveSearch
     }
 
     /**
-     * Safely retrieve the queryString.
-     *
-     * @param string $tableName
-     * @return string
-     */
-    public function getQueryString($tableName = '')
-    {
-        return $GLOBALS['TYPO3_DB']->quoteStr($this->queryString, $tableName);
-    }
-
-    /**
      * Setter for limit value.
      *
      * @param int $limitCount
index caf41e3..83c2941 100644 (file)
@@ -14,8 +14,10 @@ namespace TYPO3\CMS\Core\Utility;
  * The TYPO3 project - inspiring people to share!
  */
 
+use Doctrine\DBAL\DBALException;
 use TYPO3\CMS\Core\Database\ConnectionPool;
 use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
+use TYPO3\CMS\Core\Database\Query\Restriction\HiddenRestriction;
 use TYPO3\CMS\Frontend\Page\PageRepository;
 
 /**
@@ -112,11 +114,6 @@ class RootlineUtility
     protected static $pageRecordCache = array();
 
     /**
-     * @var \TYPO3\CMS\Core\Database\DatabaseConnection
-     */
-    protected $databaseConnection;
-
-    /**
      * @param int $uid
      * @param string $mountPointParameter
      * @param \TYPO3\CMS\Frontend\Page\PageRepository $context
@@ -161,7 +158,6 @@ class RootlineUtility
         }
         self::$rootlineFields = array_merge(self::$rootlineFields, GeneralUtility::trimExplode(',', $GLOBALS['TYPO3_CONF_VARS']['FE']['addRootLineFields'], true));
         self::$rootlineFields = array_unique(self::$rootlineFields);
-        $this->databaseConnection = $GLOBALS['TYPO3_DB'];
 
         $this->cacheIdentifier = $this->getCacheIdentifier();
     }
@@ -286,6 +282,8 @@ class RootlineUtility
     protected function enrichWithRelationFields($uid, array $pageRecord)
     {
         $pageOverlayFields = GeneralUtility::trimExplode(',', $GLOBALS['TYPO3_CONF_VARS']['FE']['pageOverlayFields']);
+        $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
+
         foreach ($GLOBALS['TCA']['pages']['columns'] as $column => $configuration) {
             if ($this->columnHasRelationToResolve($configuration)) {
                 $configuration = $configuration['config'];
@@ -306,32 +304,41 @@ class RootlineUtility
                 } else {
                     $columnIsOverlaid = in_array($column, $pageOverlayFields, true);
                     $table = $configuration['foreign_table'];
-                    $field = $configuration['foreign_field'];
-                    $whereClauseParts = array($field . ' = ' . (int)($columnIsOverlaid ? $uid : $pageRecord['uid']));
+
+                    $queryBuilder = $connectionPool->getQueryBuilderForTable($table);
+                    $queryBuilder->getRestrictions()->removeAll()
+                        ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
+                        ->add(GeneralUtility::makeInstance(HiddenRestriction::class));
+                    $queryBuilder->select('uid')
+                        ->from($table)
+                        ->where(
+                            $queryBuilder->expr()->eq($configuration['foreign_field'], (int)($columnIsOverlaid ? $uid : $pageRecord['uid']))
+                        );
+
                     if (isset($configuration['foreign_match_fields']) && is_array($configuration['foreign_match_fields'])) {
                         foreach ($configuration['foreign_match_fields'] as $field => $value) {
-                            $whereClauseParts[] = $field . ' = ' . $this->databaseConnection->fullQuoteStr($value, $table);
+                            $queryBuilder->andWhere(
+                                $queryBuilder->expr()->eq($field, $queryBuilder->createNamedParameter($value))
+                            );
                         }
                     }
                     if (isset($configuration['foreign_table_field'])) {
-                        if ((int)$this->languageUid > 0 && $columnIsOverlaid) {
-                            $whereClauseParts[] = trim($configuration['foreign_table_field']) . ' = \'pages_language_overlay\'';
-                        } else {
-                            $whereClauseParts[] = trim($configuration['foreign_table_field']) . ' = \'pages\'';
-                        }
+                        $queryBuilder->andWhere(
+                            $queryBuilder->expr()->eq(
+                                trim($configuration['foreign_table_field']),
+                                $queryBuilder->createNamedParameter((int)$this->languageUid > 0 && $columnIsOverlaid ? 'pages_language_overlay' : 'pages'))
+                        );
                     }
-                    if (isset($GLOBALS['TCA'][$table]['ctrl']['enablecolumns']['disabled'])) {
-                        $whereClauseParts[] = $table . '.' . $GLOBALS['TCA'][$table]['ctrl']['enablecolumns']['disabled'] . ' = 0';
+                    if (isset($configuration['foreign_sortby'])) {
+                        $queryBuilder->orderBy($configuration['foreign_sortby']);
                     }
-                    $whereClause = implode(' AND ', $whereClauseParts);
-                    $whereClause .= $this->pageContext->deleteClause($table);
-                    $orderBy = isset($configuration['foreign_sortby']) ? $configuration['foreign_sortby'] : '';
-                    $rows = $this->databaseConnection->exec_SELECTgetRows('uid', $table, $whereClause, '', $orderBy);
-                    if (!is_array($rows)) {
-                        throw new \RuntimeException('Could to resolve related records for page ' . $uid . ' and foreign_table ' . htmlspecialchars($configuration['foreign_table']), 1343589452);
+                    try {
+                        $statement = $queryBuilder->execute();
+                    } catch (DBALException $e) {
+                        throw new \RuntimeException('Could to resolve related records for page ' . $uid . ' and foreign_table ' . htmlspecialchars($table), 1343589452);
                     }
-                    $relatedUids = array();
-                    foreach ($rows as $row) {
+                    $relatedUids = [];
+                    while ($row = $statement->fetch()) {
                         $relatedUids[] = $row['uid'];
                     }
                 }
diff --git a/typo3/sysext/core/Documentation/Changelog/master/Breaking-77587-RemovedLiveSearch-getQueryString.rst b/typo3/sysext/core/Documentation/Changelog/master/Breaking-77587-RemovedLiveSearch-getQueryString.rst
new file mode 100644 (file)
index 0000000..10a7d34
--- /dev/null
@@ -0,0 +1,26 @@
+=====================================================
+Breaking: #77587 - Removed LiveSearch->getQueryString
+=====================================================
+
+Description
+===========
+
+The public utility method ``getQueryString()`` within the ``LiveSearch`` PHP class has been removed.
+
+
+Impact
+======
+
+Calling the method directly will result in a PHP fatal error.
+
+
+Affected Installations
+======================
+
+Any installation extending TYPO3's internal LiveSearch functionality via an extension.
+
+
+Migration
+=========
+
+Use one of the various quoting options shipped with the Doctrine DBAL.
\ No newline at end of file
index 6293b38..ff95897 100644 (file)
@@ -286,43 +286,4 @@ class RootlineUtilityTest extends \TYPO3\CMS\Core\Tests\UnitTestCase
         $this->subject->__construct(42, '47-11,48-12', $this->pageContextMock);
         $this->assertTrue($cacheFrontendMock->isValidEntryIdentifier($this->subject->getCacheIdentifier()));
     }
-
-    /**
-     * @test
-     */
-    public function enrichWithRelationFieldsCreatesWhereClauseForDisabledField()
-    {
-        $mockDatabaseConnection = $this->getMockBuilder(\TYPO3\CMS\Core\Database\DatabaseConnection::class)
-            ->setMethods(array('exec_SELECTgetRows'))
-            ->disableOriginalConstructor()
-            ->getMock();
-        $subject = $this->getAccessibleMock(\TYPO3\CMS\Core\Utility\RootlineUtility::class, array('columnHasRelationToResolve'), array(1, '', $this->pageContextMock));
-        $subject->_set('databaseConnection', $mockDatabaseConnection);
-        $GLOBALS['TYPO3_CONF_VARS']['FE']['pageOverlayFields'] = '';
-        $foreign_table = $this->getUniqueId('foreign_table');
-        $foreign_field = $this->getUniqueId('foreign_field');
-        $GLOBALS['TCA'][$foreign_table]['ctrl']['enablecolumns']['disabled'] = $this->getUniqueId('disabled');
-        $GLOBALS['TCA']['pages']['columns'] = array(
-            'test' => array(
-                'config' => array(
-                    'foreign_table' => $foreign_table,
-                    'foreign_field' => $foreign_field
-                )
-            )
-        );
-        $expected = array(
-            $foreign_field . ' = 0',
-            $foreign_table . '.' . $GLOBALS['TCA'][$foreign_table]['ctrl']['enablecolumns']['disabled'] . ' = 0'
-        );
-        $this->pageContextMock->expects($this->once())->method('deleteClause')->will($this->returnValue(''));
-        $mockDatabaseConnection->expects(
-            $this->once())->
-            method('exec_SELECTgetRows')->
-            with('uid', $foreign_table, implode(' AND ', $expected), '', '', '', '')->
-            // the return value does not matter much, it is only here to prevent error messages from further code execution
-            will($this->returnValue(array('uid' => 17))
-        );
-        $subject->expects($this->once())->method('columnHasRelationToResolve')->will($this->returnValue(true));
-        $subject->_call('enrichWithRelationFields', 17, array());
-    }
 }
index fe1a7b1..23bafc9 100644 (file)
@@ -15,6 +15,7 @@ namespace TYPO3\CMS\Frontend\ContentObject\Menu;
  */
 
 use TYPO3\CMS\Core\Cache\CacheManager;
+use TYPO3\CMS\Core\Database\ConnectionPool;
 use TYPO3\CMS\Core\Database\DatabaseConnection;
 use TYPO3\CMS\Core\Database\RelationHandler;
 use TYPO3\CMS\Core\TimeTracker\TimeTracker;
@@ -964,13 +965,14 @@ abstract class AbstractMenuContentObject
         if ($kw && $startUid) {
             $bA = MathUtility::forceIntegerInRange($this->conf['special.']['beginAtLevel'], 0, 100);
             $id_list = $this->parent_cObj->getTreeList(-1 * $startUid, $depth - 1 + $bA, $bA - 1);
-            $kwArr = explode(',', $kw);
+            $kwArr = GeneralUtility::trimExplode(',', $kw, true);
             $keyWordsWhereArr = array();
+            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
             foreach ($kwArr as $word) {
-                $word = trim($word);
-                if ($word) {
-                    $keyWordsWhereArr[] = $kfield . ' LIKE \'%' . $this->getDatabaseConnection()->quoteStr($word, 'pages') . '%\'';
-                }
+                $keyWordsWhereArr[] = $queryBuilder->expr()->like(
+                    $kfield,
+                    $queryBuilder->quote('%' . $queryBuilder->escapeLikeWildcards($word) . '%')
+                );
             }
             $where = empty($keyWordsWhereArr) ? '' : '(' . implode(' OR ', $keyWordsWhereArr) . ')';
             $res = $this->parent_cObj->exec_getQuery('pages', array(