diff --git a/dev/tests/integration/etc/di/preferences/ce.php b/dev/tests/integration/etc/di/preferences/ce.php new file mode 100644 index 0000000000000..52573b8d3d289 --- /dev/null +++ b/dev/tests/integration/etc/di/preferences/ce.php @@ -0,0 +1,21 @@ + 'Magento\TestFramework\CookieManager', + 'Magento\Framework\ObjectManager\DynamicConfigInterface' => + '\Magento\TestFramework\ObjectManager\Configurator', + 'Magento\Framework\App\RequestInterface' => 'Magento\TestFramework\Request', + 'Magento\Framework\App\Request\Http' => 'Magento\TestFramework\Request', + 'Magento\Framework\App\ResponseInterface' => 'Magento\TestFramework\Response', + 'Magento\Framework\App\Response\Http' => 'Magento\TestFramework\Response', + 'Magento\Framework\Interception\PluginListInterface' => + 'Magento\TestFramework\Interception\PluginList', + 'Magento\Framework\Interception\ObjectManager\Config' => + 'Magento\TestFramework\ObjectManager\Config', + 'Magento\Framework\View\LayoutInterface' => 'Magento\TestFramework\View\Layout', + 'Magento\Framework\App\Resource\ConnectionAdapterInterface' => 'Magento\TestFramework\Db\ConnectionAdapter', +]; diff --git a/dev/tests/integration/framework/Magento/TestFramework/ObjectManagerFactory.php b/dev/tests/integration/framework/Magento/TestFramework/ObjectManagerFactory.php index 763011a315b36..54d587be455fe 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/ObjectManagerFactory.php +++ b/dev/tests/integration/framework/Magento/TestFramework/ObjectManagerFactory.php @@ -6,6 +6,7 @@ namespace Magento\TestFramework; use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Filesystem\DriverPool; /** @@ -89,24 +90,21 @@ protected function _loadPrimaryConfig(DirectoryList $directoryList, $driverPool, 'default_setup' => ['type' => 'Magento\TestFramework\Db\ConnectionAdapter'] ] ); + $diPreferences = []; + $diPreferencesPath = __DIR__ . '/../../../etc/di/preferences/'; + + $preferenceFiles = glob($diPreferencesPath . '*.php'); + + foreach ($preferenceFiles as $file) { + if (!is_readable($file)) { + throw new LocalizedException(__("'%1' is not readable file.", $file)); + } + $diPreferences = array_replace($diPreferences, include $file); + } + $this->_primaryConfigData['preferences'] = array_replace( $this->_primaryConfigData['preferences'], - [ - 'Magento\Framework\Stdlib\CookieManagerInterface' => 'Magento\TestFramework\CookieManager', - 'Magento\Framework\ObjectManager\DynamicConfigInterface' => - '\Magento\TestFramework\ObjectManager\Configurator', - 'Magento\Framework\App\RequestInterface' => 'Magento\TestFramework\Request', - 'Magento\Framework\App\Request\Http' => 'Magento\TestFramework\Request', - 'Magento\Framework\App\ResponseInterface' => 'Magento\TestFramework\Response', - 'Magento\Framework\App\Response\Http' => 'Magento\TestFramework\Response', - 'Magento\Framework\Interception\PluginListInterface' => - 'Magento\TestFramework\Interception\PluginList', - 'Magento\Framework\Interception\ObjectManager\Config' => - 'Magento\TestFramework\ObjectManager\Config', - 'Magento\Framework\View\LayoutInterface' => 'Magento\TestFramework\View\Layout', - 'Magento\Framework\App\Resource\ConnectionAdapterInterface' => - 'Magento\TestFramework\Db\ConnectionAdapter', - ] + $diPreferences ); } return $this->_primaryConfigData; diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Resource/ConnectionFactoryTest.php b/dev/tests/integration/testsuite/Magento/Framework/App/Resource/ConnectionFactoryTest.php index 90da5fc6deb9d..5b77db64a1c62 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/App/Resource/ConnectionFactoryTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Resource/ConnectionFactoryTest.php @@ -34,7 +34,6 @@ public function testCreate() ]; $connection = $this->model->create($dbConfig); $this->assertInstanceOf('\Magento\Framework\DB\Adapter\AdapterInterface', $connection); - $this->assertAttributeInstanceOf('\Magento\Framework\Cache\FrontendInterface', '_cacheAdapter', $connection); $this->assertAttributeInstanceOf('\Magento\Framework\Db\LoggerInterface', 'logger', $connection); } } diff --git a/dev/tests/static/framework/Magento/TestFramework/Utility/CodeCheck.php b/dev/tests/static/framework/Magento/TestFramework/Utility/CodeCheck.php new file mode 100644 index 0000000000000..907a972becd04 --- /dev/null +++ b/dev/tests/static/framework/Magento/TestFramework/Utility/CodeCheck.php @@ -0,0 +1,150 @@ +|function\s)' . $quotedMethod . '\s*\(/iS', + $content + ); + } + // without opening parentheses to match static callbacks notation + if (self::_isRegexpMatched( + '/' . preg_quote($class, '/') . '::\s*' . $quotedMethod . '[^a-z\d_]/iS', + $content + ) + ) { + return true; + } + if (self::isClassOrInterface($content, $class) || self::isDirectDescendant($content, $class)) { + return self::_isRegexpMatched('/this->' . $quotedMethod . '\s*\(/iS', $content) + || self::_isRegexpMatched( + '/(self|static|parent)::\s*' . $quotedMethod . '\s*\(/iS', + $content + ); + } + } + + /** + * Check if methods or functions are used in the content + * + * If class context is specified, only the class and its descendants will be checked. + * + * @param string $property + * @param string $content + * @param string $class + * @return bool + */ + public static function isPropertyUsed($property, $content, $class = null) + { + if ($class) { + if (!self::isClassOrInterface($content, $class) && !self::isDirectDescendant($content, $class)) { + return false; + } + } + return self::_isRegexpMatched( + '/[^a-z\d_]' . preg_quote($property, '/') . '[^a-z\d_]/iS', + $content + ); + } + + /** + * Analyze content to determine whether it is a declaration of the specified class/interface + * + * @param string $content + * @param string $name + * @return bool + */ + public static function isClassOrInterface($content, $name) + { + return self::_isRegexpMatched('/\b(?:class|interface)\s+' . preg_quote($name, '/') . '\b[^{]*\{/iS', $content); + } + + /** + * Analyze content to determine whether this is a direct descendant of the specified class/interface + * + * @param string $content + * @param string $name + * @return bool + */ + public static function isDirectDescendant($content, $name) + { + $name = preg_quote($name, '/'); + return self::_isRegexpMatched( + '/\s+extends\s+' . $name . '\b|\s+implements\s+[^{]*\b' . $name . '\b[^{^\\\\]*\{/iS', + $content + ); + } + + /** + * Check if content matches the regexp + * + * @param string $regexp + * @param string $content + * @throws \Exception + * @return bool True if the content matches the regexp + */ + protected static function _isRegexpMatched($regexp, $content) + { + $result = preg_match($regexp, $content); + if ($result === false) { + throw new \Exception('An error occurred when matching regexp "' . $regexp . '""'); + } + return 1 === $result; + } +} diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/ObsoleteCodeTest.php b/dev/tests/static/testsuite/Magento/Test/Legacy/ObsoleteCodeTest.php index a6a2253c57e5d..091472e472f40 100644 --- a/dev/tests/static/testsuite/Magento/Test/Legacy/ObsoleteCodeTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/ObsoleteCodeTest.php @@ -12,11 +12,6 @@ class ObsoleteCodeTest extends \PHPUnit_Framework_TestCase { - /** - * Message text that is used to render suggestions - */ - const SUGGESTION_MESSAGE = 'Use "%s" instead.'; - /**@#+ * Lists of obsolete entities from fixtures * @@ -427,11 +422,11 @@ protected function _testObsoleteConstants($content) list($constant, $class, $replacement) = $row; if ($class) { $class = ltrim($class, '\\'); - $this->checkConstantWithFullClasspath($constant, $class, $replacement, $content); - $this->checkConstantWithClasspath($constant, $class, $replacement, $content); + $this->_checkConstantWithFullClasspath($constant, $class, $replacement, $content); + $this->_checkConstantWithClasspath($constant, $class, $replacement, $content); } else { $regex = '\b' . preg_quote($constant, '/') . '\b'; - $this->checkExistenceOfObsoleteConstants($regex, '', $content, $constant, $replacement, $class); + $this->_checkExistenceOfObsoleteConstants($regex, '', $content, $constant, $replacement, $class); } } } @@ -469,11 +464,11 @@ private function buildRegExFromObsoleteConstant($classPartialPath, $content, $co * @param string $replacement * @param string $content */ - private function checkConstantWithFullClasspath($constant, $class, $replacement, $content) + private function _checkConstantWithFullClasspath($constant, $class, $replacement, $content) { $constantRegex = preg_quote($constant, '/'); $classRegex = preg_quote($class); - $this->checkExistenceOfObsoleteConstants( + $this->_checkExistenceOfObsoleteConstants( $constantRegex, $classRegex, $content, @@ -491,7 +486,7 @@ private function checkConstantWithFullClasspath($constant, $class, $replacement, * @param string $replacement * @param string $content */ - private function checkConstantWithClasspath($constant, $class, $replacement, $content) + private function _checkConstantWithClasspath($constant, $class, $replacement, $content) { $classPathParts = explode('\\', $class); $classPartialPath = ''; @@ -521,7 +516,7 @@ private function checkConstantWithClasspath($constant, $class, $replacement, $co $this->_suggestReplacement(sprintf("Constant '%s' is obsolete.", $constant), $replacement) ); } else { - $this->checkExistenceOfObsoleteConstants( + $this->_checkExistenceOfObsoleteConstants( $constantRegex, $classRegex, $content, @@ -543,7 +538,7 @@ private function checkConstantWithClasspath($constant, $class, $replacement, $co * @param string $replacement * @param string $class */ - private function checkExistenceOfObsoleteConstants( + private function _checkExistenceOfObsoleteConstants( $constantRegex, $classRegex, $content, @@ -562,14 +557,14 @@ private function checkExistenceOfObsoleteConstants( $matchClass = preg_match($classRegexFull, $content, $matchClassString); if ($matchClass === 1) { if ($matchClassString['classAlias']) { - $result = $this->checkAliasUseNamespace( + $result = $this->_checkAliasUseNamespace( $constantRegex, $matchConstantString, $matchClassString, $class ); } else { - $result = $this->checkNoAliasUseNamespace($matchConstantString, $matchClassString, $class); + $result = $this->_checkNoAliasUseNamespace($matchConstantString, $matchClassString, $class); } } else { foreach ($matchConstantString['classWithConst'] as $constantMatch) { @@ -600,7 +595,7 @@ private function checkExistenceOfObsoleteConstants( * @param string $class * @return int */ - private function checkAliasUseNamespace( + private function _checkAliasUseNamespace( $constantRegex, $matchConstantString, $matchClassString, @@ -618,7 +613,7 @@ private function checkAliasUseNamespace( $foundAsComponent = true; } if (strpos($constantMatch, '::') !== false) { - $foundProperUse = $this->checkCompletePathOfClass( + $foundProperUse = $this->_checkCompletePathOfClass( $constantMatch, $matchClassString, $class, @@ -645,14 +640,14 @@ private function checkAliasUseNamespace( * @param string $class * @return int */ - private function checkNoAliasUseNamespace( + private function _checkNoAliasUseNamespace( $matchConstantString, $matchClassString, $class ) { $foundProperUse = false; foreach ($matchConstantString['constPart'] as $constantMatch) { - $foundProperUse = $this->checkCompletePathOfClass( + $foundProperUse = $this->_checkCompletePathOfClass( $constantMatch, $matchClassString, $class @@ -678,7 +673,7 @@ private function checkNoAliasUseNamespace( * @param string $asComponent * @return bool */ - private function checkCompletePathOfClass( + private function _checkCompletePathOfClass( $constantMatch, $matchClassString, $class, @@ -706,7 +701,7 @@ private function checkCompletePathOfClass( ), '\\' )); - if ($this->checkClasspathProperDivisionNoConstantPath( + if ($this->_checkClasspathProperDivisionNoConstantPath( $pathInUseNamespaceTruncated, $pathInUseNamespace, $matchClassString, @@ -715,7 +710,7 @@ private function checkCompletePathOfClass( )) { return true; } else { - return $this->checkClasspathProperDivisionWithConstantPath( + return $this->_checkClasspathProperDivisionWithConstantPath( $pathInUseNamespaceTruncated, $pathInUseNamespace, $pathWithConst, @@ -735,7 +730,7 @@ private function checkCompletePathOfClass( * @param bool $foundAsComponent * @return bool */ - private function checkClasspathProperDivisionNoConstantPath( + private function _checkClasspathProperDivisionNoConstantPath( $pathInUseNamespaceTruncated, $pathInUseNamespace, $matchClassString, @@ -760,7 +755,7 @@ private function checkClasspathProperDivisionNoConstantPath( * @param bool $foundAsComponent * @return bool */ - private function checkClasspathProperDivisionWithConstantPath( + private function _checkClasspathProperDivisionWithConstantPath( $pathInUseNamespaceTruncated, $pathInUseNamespace, $pathWithConst, diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/RestrictedCodeTest.php b/dev/tests/static/testsuite/Magento/Test/Legacy/RestrictedCodeTest.php new file mode 100644 index 0000000000000..12b052cab56f1 --- /dev/null +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/RestrictedCodeTest.php @@ -0,0 +1,127 @@ +getPathToSource(), '', $file) + ); + array_push(self::$_fixtureFiles, $relativePath); + $data = array_merge_recursive($data, self::_readList($file)); + } + } + + /** + * Isolate including a file into a method to reduce scope + * + * @param string $file + * @return array + */ + protected static function _readList($file) + { + return include $file; + } + + /** + * Test that restricted entities are not used in PHP files + * @return void + */ + public function testPhpFiles() + { + $invoker = new \Magento\Framework\App\Utility\AggregateInvoker($this); + $testFiles = \Magento\TestFramework\Utility\ChangedFiles::getPhpFiles(__DIR__ . '/_files/changed_files*'); + foreach (self::$_fixtureFiles as $fixtureFile) { + if (array_key_exists($fixtureFile, $testFiles)) { + unset($testFiles[$fixtureFile]); + } + } + $invoker( + function ($file) { + $this->_testRestrictedClasses($file); + }, + $testFiles + ); + } + + /** + * Assert that restricted classes are not used in the file + * + * @param string $file + * @return void + */ + protected function _testRestrictedClasses($file) + { + $content = file_get_contents($file); + foreach (self::$_classes as $restrictedClass => $classRules) { + foreach ($classRules['exclude'] as $skippedPath) { + if ($this->_isFileInPath($skippedPath, $file)) { + continue 2; + } + } + $this->assertFalse( + \Magento\TestFramework\Utility\CodeCheck::isClassUsed($restrictedClass, $content), + sprintf( + "Class '%s' is restricted. Suggested replacement: %s", + $restrictedClass, + $classRules['replacement'] + ) + ); + } + } + + /** + * Checks if the file path (relative to Magento folder) starts with the given path + * + * @param string $subPath + * @param string $file + * @return bool + */ + protected function _isFileInPath($subPath, $file) + { + $relativePath = str_replace(\Magento\Framework\App\Utility\Files::init()->getPathToSource(), '', $file); + return 0 === strpos($relativePath, $subPath); + } +} diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/restricted_classes.php b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/restricted_classes.php new file mode 100644 index 0000000000000..2ccf17ec4386e --- /dev/null +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/restricted_classes.php @@ -0,0 +1,27 @@ + will be suggested to be used instead. + * Use to specify files and directories that are allowed to use restricted classes. + * + * Format: array(, [, array()]]) + * + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +return [ + 'Zend_Db_Select' => [ + 'replacement' => '\Magento\Framework\DB\Select', + 'exclude' => [ + '/lib/internal/Magento/Framework/DB/Select.php', + '/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php', + '/lib/internal/Magento/Framework/Model/Resource/Iterator.php', + ] + ], + 'Zend_Db_Adapter_Pdo_Mysql' => [ + 'replacement' => '\Magento\Framework\DB\Adapter\Pdo\Mysql', + 'exclude' => [ + '/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php', + ] + ], +]; diff --git a/lib/internal/Magento/Framework/App/Request/Http.php b/lib/internal/Magento/Framework/App/Request/Http.php index 26c41a6abc229..fed64841c76f9 100644 --- a/lib/internal/Magento/Framework/App/Request/Http.php +++ b/lib/internal/Magento/Framework/App/Request/Http.php @@ -7,6 +7,7 @@ use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\App\RequestInterface; +use Magento\Framework\App\RequestSafetyInterface; use Magento\Framework\App\Route\ConfigInterface\Proxy as ConfigInterface; use Magento\Framework\HTTP\PhpEnvironment\Request; use Magento\Framework\ObjectManagerInterface; @@ -16,7 +17,7 @@ /** * Http request */ -class Http extends Request implements RequestInterface +class Http extends Request implements RequestInterface, RequestSafetyInterface { /**#@+ * HTTP Ports @@ -79,6 +80,16 @@ class Http extends Request implements RequestInterface */ protected $objectManager; + /** + * @var bool|null + */ + protected $isSafeMethod = null; + + /** + * @var array + */ + protected $safeRequestTypes = ['GET', 'HEAD', 'TRACE', 'OPTIONS']; + /** * @param CookieReaderInterface $cookieReader * @param StringUtils $converter @@ -416,6 +427,21 @@ public function isSecure() return $this->initialRequestSecure($offLoaderHeader); } + /** + * {@inheritdoc} + */ + public function isSafeMethod() + { + if ($this->isSafeMethod === null) { + if (isset($_SERVER['REQUEST_METHOD']) && (in_array($_SERVER['REQUEST_METHOD'], $this->safeRequestTypes))) { + $this->isSafeMethod = true; + } else { + $this->isSafeMethod = false; + } + } + return $this->isSafeMethod; + } + /** * Checks if the immediate request is delivered over HTTPS * diff --git a/lib/internal/Magento/Framework/App/RequestSafetyInterface.php b/lib/internal/Magento/Framework/App/RequestSafetyInterface.php new file mode 100644 index 0000000000000..d4e8f113f3d93 --- /dev/null +++ b/lib/internal/Magento/Framework/App/RequestSafetyInterface.php @@ -0,0 +1,24 @@ +_config = $resourceConfig; - $this->_connectionFactory = $connectionFactory; + $this->config = $resourceConfig; + $this->connectionFactory = $connectionFactory; $this->deploymentConfig = $deploymentConfig; - $this->_tablePrefix = $tablePrefix ?: null; + $this->tablePrefix = $tablePrefix ?: null; } /** * Retrieve connection to resource specified by $resourceName * * @param string $resourceName - * @return \Magento\Framework\DB\Adapter\AdapterInterface|false + * @return \Magento\Framework\DB\Adapter\AdapterInterface + * @throws \DomainException * @codeCoverageIgnore */ public function getConnection($resourceName = self::DEFAULT_CONNECTION) { - $connectionName = $this->_config->getConnectionName($resourceName); + $connectionName = $this->config->getConnectionName($resourceName); return $this->getConnectionByName($connectionName); } @@ -95,12 +96,13 @@ public function getConnection($resourceName = self::DEFAULT_CONNECTION) * Retrieve connection by $connectionName * * @param string $connectionName - * @return bool|\Magento\Framework\DB\Adapter\AdapterInterface + * @return \Magento\Framework\DB\Adapter\AdapterInterface + * @throws \DomainException */ public function getConnectionByName($connectionName) { - if (isset($this->_connections[$connectionName])) { - return $this->_connections[$connectionName]; + if (isset($this->connections[$connectionName])) { + return $this->connections[$connectionName]; } $connectionConfig = $this->deploymentConfig->get( @@ -108,13 +110,12 @@ public function getConnectionByName($connectionName) ); if ($connectionConfig) { - $connection = $this->_connectionFactory->create($connectionConfig); - } - if (empty($connection)) { - return false; + $connection = $this->connectionFactory->create($connectionConfig); + } else { + throw new \DomainException('Connection "' . $connectionName . '" is not defined'); } - $this->_connections[$connectionName] = $connection; + $this->connections[$connectionName] = $connection; return $connection; } @@ -174,7 +175,7 @@ public function getTriggerName($tableName, $time, $event) */ public function setMappedTableName($tableName, $mappedName) { - $this->_mappedTableNames[$tableName] = $mappedName; + $this->mappedTableNames[$tableName] = $mappedName; return $this; } @@ -186,8 +187,8 @@ public function setMappedTableName($tableName, $mappedName) */ public function getMappedTableName($tableName) { - if (isset($this->_mappedTableNames[$tableName])) { - return $this->_mappedTableNames[$tableName]; + if (isset($this->mappedTableNames[$tableName])) { + return $this->mappedTableNames[$tableName]; } else { return false; } @@ -240,11 +241,11 @@ public function getFkName($priTableName, $priColumnName, $refTableName, $refColu */ private function getTablePrefix() { - if (null === $this->_tablePrefix) { - $this->_tablePrefix = (string)$this->deploymentConfig->get( + if (null === $this->tablePrefix) { + $this->tablePrefix = (string)$this->deploymentConfig->get( ConfigOptionsListConstants::CONFIG_PATH_DB_PREFIX ); } - return $this->_tablePrefix; + return $this->tablePrefix; } } diff --git a/lib/internal/Magento/Framework/App/Resource/Config.php b/lib/internal/Magento/Framework/App/Resource/Config.php index 27a611265bc06..561718c5a6643 100644 --- a/lib/internal/Magento/Framework/App/Resource/Config.php +++ b/lib/internal/Magento/Framework/App/Resource/Config.php @@ -54,6 +54,8 @@ public function getConnectionName($resourceName) { $connectionName = \Magento\Framework\App\Resource::DEFAULT_CONNECTION; + $resourceName = preg_replace("/_setup$/", '', $resourceName); + if (!isset($this->_connectionNames[$resourceName])) { $resourcesConfig = $this->get(); $pointerResourceName = $resourceName; diff --git a/lib/internal/Magento/Framework/App/Test/Unit/Request/HttpTest.php b/lib/internal/Magento/Framework/App/Test/Unit/Request/HttpTest.php index 3bd82cb2d54ec..c6c309f0fd55a 100644 --- a/lib/internal/Magento/Framework/App/Test/Unit/Request/HttpTest.php +++ b/lib/internal/Magento/Framework/App/Test/Unit/Request/HttpTest.php @@ -94,7 +94,7 @@ private function getModel($uri = null) public function testGetOriginalPathInfoWithTestUri() { - $uri = 'http://test.com/value'; + $uri = 'http://test.com/value?key=value'; $this->_model = $this->getModel($uri); $this->assertEquals('/value', $this->_model->getOriginalPathInfo()); } @@ -350,6 +350,52 @@ public function testIsSecure($isSecure, $serverHttps, $headerOffloadKey, $header $this->assertSame($isSecure, $this->_model->isSecure()); } + /** + * @dataProvider httpSafeMethodProvider + * @backupGlobals enabled + * @param string $method value of $_SERVER['REQUEST_METHOD'] + */ + public function testIsSafeMethodTrue($httpMethod) + { + $this->_model = $this->getModel(); + $_SERVER['REQUEST_METHOD'] = $httpMethod; + $this->assertEquals(true, $this->_model->IsSafeMethod()); + } + + /** + * @dataProvider httpNotSafeMethodProvider + * @backupGlobals enabled + * @param string $method value of $_SERVER['REQUEST_METHOD'] + */ + public function testIsSafeMethodFalse($httpMethod) + { + $this->_model = $this->getModel(); + $_SERVER['REQUEST_METHOD'] = $httpMethod; + $this->assertEquals(false, $this->_model->IsSafeMethod()); + } + + public function httpSafeMethodProvider() + { + return [ + 'Test 1' => ['GET'], + 'Test 2' => ['HEAD'], + 'Test 3' => ['TRACE'], + 'Test 4' => ['OPTIONS'] + ]; + } + + public function httpNotSafeMethodProvider() + { + return [ + 'Test 1' => ['POST'], + 'Test 2' => ['PUT'], + 'Test 3' => ['DELETE'], + 'Test 4' => ['PATCH'], + 'Test 5' => ['CONNECT'], + 'Test 6' => [null] + ]; + } + public function isSecureDataProvider() { /** diff --git a/lib/internal/Magento/Framework/App/Test/Unit/ResourceTest.php b/lib/internal/Magento/Framework/App/Test/Unit/ResourceTest.php index e579bc576bad7..f48b84c57b5b4 100644 --- a/lib/internal/Magento/Framework/App/Test/Unit/ResourceTest.php +++ b/lib/internal/Magento/Framework/App/Test/Unit/ResourceTest.php @@ -4,12 +4,11 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - namespace Magento\Framework\App\Test\Unit; -use \Magento\Framework\App\Resource; +use Magento\Framework\App\Resource; use Magento\Framework\Config\ConfigOptionsListConstants; +use Magento\Framework\Model\Resource\Type\Db\ConnectionFactoryInterface; class ResourceTest extends \PHPUnit_Framework_TestCase { @@ -20,12 +19,12 @@ class ResourceTest extends \PHPUnit_Framework_TestCase /** * @var \Magento\Framework\App\Resource\ConfigInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $_config; + protected $config; /** - * @var \Magento\Framework\Model\Resource\Type\Db\ConnectionFactory|\PHPUnit_Framework_MockObject_MockObject + * @var ConnectionFactoryInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $_connectionFactory; + protected $connectionFactory; /** * @var \Magento\Framework\App\DeploymentConfig|\PHPUnit_Framework_MockObject_MockObject @@ -44,36 +43,39 @@ class ResourceTest extends \PHPUnit_Framework_TestCase public function setUp() { - $this->_connectionFactory = $this->getMockBuilder('Magento\Framework\Model\Resource\Type\Db\ConnectionFactory') - ->disableOriginalConstructor() + $this->connectionFactory = $this->getMockBuilder(ConnectionFactoryInterface::class) ->setMethods(['create']) - ->getMock(); - $this->_config = $this->getMockBuilder('Magento\Framework\App\Resource\ConfigInterface') + ->getMockForAbstractClass(); + $this->config = $this->getMockBuilder('Magento\Framework\App\Resource\ConfigInterface') ->disableOriginalConstructor() ->setMethods(['getConnectionName']) ->getMock(); - $this->_config->expects($this->any()) + $this->config->expects($this->any()) ->method('getConnectionName') ->with(self::RESOURCE_NAME) ->will($this->returnValue(self::CONNECTION_NAME)); $this->deploymentConfig = $this->getMock('Magento\Framework\App\DeploymentConfig', [], [], '', false); - $this->deploymentConfig->expects($this->any()) + $this->deploymentConfig + ->expects($this->any()) ->method('get') - ->will($this->returnValue( + ->willReturnMap( + [ [ - 'default' => [ - 'host' => 'localhost', - 'dbname' => 'magento', - 'username' => 'username', - ], - self::CONNECTION_NAME => [ + ConfigOptionsListConstants::CONFIG_PATH_DB_CONNECTIONS . '/connection-name', + null, + [ 'host' => 'localhost', 'dbname' => 'magento', 'username' => 'username', - ], + ] + ], + [ + ConfigOptionsListConstants::CONFIG_PATH_DB_PREFIX, + null, + self::TABLE_PREFIX ] - ) + ] ); $this->connection = $this->getMockForAbstractClass('Magento\Framework\DB\Adapter\AdapterInterface'); @@ -82,24 +84,24 @@ public function setUp() ->will($this->returnArgument(0)); $this->resource = new Resource( - $this->_config, - $this->_connectionFactory, - $this->deploymentConfig, - self::TABLE_PREFIX + $this->config, + $this->connectionFactory, + $this->deploymentConfig ); } + /** + * @expectedException \DomainException + * @expectedExceptionMessage Connection "invalid" is not defined + */ public function testGetConnectionFail() { - $this->_connectionFactory->expects($this->once()) - ->method('create') - ->will($this->returnValue(null)); - $this->assertFalse($this->resource->getConnection(self::RESOURCE_NAME)); + $this->resource->getConnectionByName('invalid'); } public function testGetConnectionInitConnection() { - $this->_connectionFactory->expects($this->once()) + $this->connectionFactory->expects($this->once()) ->method('create') ->will($this->returnValue($this->connection)); $this->assertSame($this->connection, $this->resource->getConnection(self::RESOURCE_NAME)); @@ -114,7 +116,7 @@ public function testGetConnectionInitConnection() */ public function testGetTableName($modelEntity, $expected) { - $this->_connectionFactory->expects($this->once()) + $this->connectionFactory->expects($this->once()) ->method('create') ->will($this->returnValue($this->connection)); $this->assertSame($expected, $this->resource->getTableName($modelEntity)); @@ -141,7 +143,7 @@ public function getTableNameDataProvider() */ public function testGetTableNameMapped($modelEntity, $tableName, $mappedName, $expected) { - $this->_connectionFactory->expects($this->once()) + $this->connectionFactory->expects($this->once()) ->method('create') ->will($this->returnValue($this->connection)); $this->resource->setMappedTableName($tableName, $mappedName); @@ -171,7 +173,7 @@ public function testGetIdxName() ->method('getIndexName') ->with($calculatedTableName, $fields, $indexType) ->will($this->returnValue($expectedIdxName)); - $this->_connectionFactory->expects($this->once()) + $this->connectionFactory->expects($this->once()) ->method('create') ->will($this->returnValue($this->connection)); @@ -191,7 +193,7 @@ public function testGetFkName() ->method('getForeignKeyName') ->with($calculatedTableName, $columnName, $calculatedRefTableName, $refColumnName) ->will($this->returnValue('fkName')); - $this->_connectionFactory->expects($this->once()) + $this->connectionFactory->expects($this->once()) ->method('create') ->will($this->returnValue($this->connection)); @@ -205,7 +207,7 @@ public function testGetTriggerName() $event = 'insert'; $triggerName = 'trg_subject_table_before_insert'; - $this->_connectionFactory->expects($this->once()) + $this->connectionFactory->expects($this->once()) ->method('create') ->will($this->returnValue($this->connection)); $this->connection->expects($this->once()) diff --git a/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php b/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php index 041d7d35b977f..e9598680f5981 100644 --- a/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php +++ b/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php @@ -177,10 +177,11 @@ class Mysql extends \Zend_Db_Adapter_Pdo_Mysql implements AdapterInterface * @var DateTime */ protected $dateTime; + /** * @var LoggerInterface */ - private $logger; + protected $logger; /** * @param \Magento\Framework\Stdlib\StringUtils|String $string @@ -334,6 +335,10 @@ protected function _connect() /** @link http://bugs.mysql.com/bug.php?id=18551 */ $this->_connection->query("SET SQL_MODE=''"); + if (isset($this->_config['initStatements'])) { + $this->query($this->_config['initStatements']); + } + if (!$this->_connectionFlagsSet) { $this->_connection->setAttribute(\PDO::ATTR_EMULATE_PREPARES, true); $this->_connection->setAttribute(\PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true); diff --git a/lib/internal/Magento/Framework/Model/Resource/Type/Db/ConnectionFactory.php b/lib/internal/Magento/Framework/Model/Resource/Type/Db/ConnectionFactory.php index 0ddbfb420bd27..3a404d045eacb 100644 --- a/lib/internal/Magento/Framework/Model/Resource/Type/Db/ConnectionFactory.php +++ b/lib/internal/Magento/Framework/Model/Resource/Type/Db/ConnectionFactory.php @@ -31,6 +31,7 @@ public function __construct(ObjectManagerInterface $objectManager) */ public function create(array $connectionConfig) { + /** @var \Magento\Framework\App\Resource\ConnectionAdapterInterface $adapterInstance */ $adapterInstance = $this->objectManager->create( \Magento\Framework\App\Resource\ConnectionAdapterInterface::class, ['config' => $connectionConfig] diff --git a/lib/internal/Magento/Framework/Search/Adapter/Mysql/TemporaryStorage.php b/lib/internal/Magento/Framework/Search/Adapter/Mysql/TemporaryStorage.php index e9f87b42890ad..d9308ddb4f10d 100644 --- a/lib/internal/Magento/Framework/Search/Adapter/Mysql/TemporaryStorage.php +++ b/lib/internal/Magento/Framework/Search/Adapter/Mysql/TemporaryStorage.php @@ -87,7 +87,7 @@ private function createTemporaryTable() $connection = $this->getConnection(); $tableName = $this->resource->getTableName(str_replace('.', '_', uniqid(self::TEMPORARY_TABLE_PREFIX, true))); $table = $connection->newTable($tableName); - $connection->dropTable($table->getName()); + $connection->dropTemporaryTable($table->getName()); $table->addColumn( self::FIELD_ENTITY_ID, Table::TYPE_INTEGER, diff --git a/lib/internal/Magento/Framework/Search/Test/Unit/Adapter/Mysql/TemporaryStorageTest.php b/lib/internal/Magento/Framework/Search/Test/Unit/Adapter/Mysql/TemporaryStorageTest.php index 4033afb052e99..f7ea0d748ab9f 100644 --- a/lib/internal/Magento/Framework/Search/Test/Unit/Adapter/Mysql/TemporaryStorageTest.php +++ b/lib/internal/Magento/Framework/Search/Test/Unit/Adapter/Mysql/TemporaryStorageTest.php @@ -143,7 +143,7 @@ private function createTemporaryTable() ->with($this->tableName) ->willReturn($table); $this->adapter->expects($this->once()) - ->method('dropTable'); + ->method('dropTemporaryTable'); $this->adapter->expects($this->once()) ->method('createTemporaryTable') ->with($table); diff --git a/setup/src/Magento/Setup/Test/Unit/Module/ConfigOptionsListTest.php b/setup/src/Magento/Setup/Test/Unit/Module/ConfigOptionsListTest.php index d1281f7c30c55..7fd4e605d8e33 100644 --- a/setup/src/Magento/Setup/Test/Unit/Module/ConfigOptionsListTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Module/ConfigOptionsListTest.php @@ -9,6 +9,7 @@ use Magento\Setup\Model\ConfigGenerator; use Magento\Setup\Model\ConfigOptionsList; use Magento\Setup\Validator\DbValidator; +use Magento\Framework\Config\ConfigOptionsListConstants; class ConfigOptionsListTest extends \PHPUnit_Framework_TestCase { @@ -100,4 +101,21 @@ public function testCreateOptionsWithOptionalNull() $configData = $this->object->createConfig([], $this->deploymentConfig); $this->assertEquals(6, count($configData)); } + + public function testValidate() + { + $options = [ + ConfigOptionsListConstants::INPUT_KEY_DB_PREFIX => 'prefix', + ConfigOptionsListConstants::INPUT_KEY_SKIP_DB_VALIDATION => false, + ConfigOptionsListConstants::INPUT_KEY_DB_NAME => 'name', + ConfigOptionsListConstants::INPUT_KEY_DB_HOST => 'host', + ConfigOptionsListConstants::INPUT_KEY_DB_USER => 'user', + ConfigOptionsListConstants::INPUT_KEY_DB_PASSWORD => 'pass' + ]; + $configDataMock = $this->getMock('Magento\Framework\Config\Data\ConfigData', [], [], '', false); + $this->dbValidator->expects($this->once())->method('checkDatabaseTablePrefix')->willReturn($configDataMock); + $this->dbValidator->expects($this->once())->method('checkDatabaseConnection')->willReturn($configDataMock); + $result = $this->object->validate($options, $this->deploymentConfig); + $this->assertEquals([], $result); + } }