diff --git a/.styleci.yml b/.styleci.yml new file mode 100644 index 00000000..4e1109ec --- /dev/null +++ b/.styleci.yml @@ -0,0 +1,5 @@ +preset: psr2 + +enabled: + - long_array_syntax + - duplicate_semicolon \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index e1459c43..57fcd311 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,6 +14,12 @@ env: - DB=pgsql PACKAGE_VERSION=high - DB=sqlite PACKAGE_VERSION=high +sudo: false + +cache: + directories: + - vendor + - $HOME/.composer/cache matrix: allow_failures: @@ -30,8 +36,8 @@ matrix: before_script: - composer selfupdate - rm -f ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/xdebug.ini - - if [[ "$PACKAGE_VERSION" == "high" ]]; then composer update --prefer-source --no-interaction; fi - - if [[ "$PACKAGE_VERSION" == "low" ]]; then composer update --prefer-lowest --prefer-source --no-interaction; fi + - if [[ "$PACKAGE_VERSION" == "high" ]]; then composer update --prefer-dist --no-interaction; fi + - if [[ "$PACKAGE_VERSION" == "low" ]]; then composer update --prefer-lowest --prefer-dist --no-interaction; fi - if [[ "$DB" == "mysql" ]]; then mysql -e "create database phpcr_tests;"; fi - if [[ "$DB" == "pgsql" ]]; then psql -c "create database phpcr_tests;" -U postgres; fi - php tests/generate_phpunit_config.php $DB diff --git a/README.md b/README.md index e85de752..a1b1c4ac 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ Set up a new database supported by Doctrine DBAL. You can use your favorite GUI ### MySQL -Note that you need at least version 5.1.5 of MySQL, otherwise you will get ``QLSTATE[42000]: Syntax error or access violation: 1305 FUNCTION cmf-app.EXTRACTVALUE does not exist`` +Note that you need at least version 5.1.5 of MySQL, otherwise you will get ``SQLSTATE[42000]: Syntax error or access violation: 1305 FUNCTION cmf-app.EXTRACTVALUE does not exist`` ```sh $ mysqladmin -u root -p create jackalope @@ -175,7 +175,7 @@ $post->setProperty("jcr:description", "This is the first post on my blog! Do you $session->save(); ``` -See [PHPCR Tutorial](https://github.com/phpcr/phpcr-docs/blob/master/tutorial/Tutorial.md) +See [PHPCR Tutorial](http://phpcr.readthedocs.org/en/latest/book/index.html) for a more detailed tutorial on how to use the PHPCR API. diff --git a/composer.json b/composer.json index 0af86216..04e5ed7f 100644 --- a/composer.json +++ b/composer.json @@ -19,7 +19,7 @@ "php": ">=5.3.3", "doctrine/dbal": ">=2.4.5,<3.0.x-dev", "phpcr/phpcr": "~2.1.2", - "phpcr/phpcr-utils": "~1.2,>=1.2.6", + "phpcr/phpcr-utils": "^1.2.8", "jackalope/jackalope": "dev-versioning as 1.3.0" }, "provide": { @@ -27,17 +27,20 @@ }, "require-dev": { "psr/log": "~1.0", - "phpcr/phpcr-api-tests": "2.1.4", + "phpcr/phpcr-api-tests": "2.1.11", "phpunit/phpunit": "4.7.*", "phpunit/dbunit": "~1.3" }, "autoload": { "psr-0": { "Jackalope\\": "src/" } }, + "autoload-dev": { + "psr-0": { "Jackalope\\": "tests/" } + }, "bin": ["bin/jackalope"], "extra": { "branch-alias": { - "dev-master": "1.2-dev" + "dev-master": "1.3-dev" } } } diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 45780577..2a8057e1 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -7,7 +7,6 @@ processIsolation="false" stopOnFailure="false" syntaxCheck="true" - strict="false" bootstrap="tests/bootstrap.php"> diff --git a/src/Jackalope/Tools/Console/Command/InitDoctrineDbalCommand.php b/src/Jackalope/Tools/Console/Command/InitDoctrineDbalCommand.php index b99284f4..7e4155ee 100644 --- a/src/Jackalope/Tools/Console/Command/InitDoctrineDbalCommand.php +++ b/src/Jackalope/Tools/Console/Command/InitDoctrineDbalCommand.php @@ -6,11 +6,10 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; - use Doctrine\DBAL\Connection; - -use Jackalope\Transport\DoctrineDBAL\RepositorySchema; +use Doctrine\DBAL\DBALException; use Doctrine\DBAL\Exception\TableNotFoundException; +use Jackalope\Transport\DoctrineDBAL\RepositorySchema; /** * Init doctrine dbal. @@ -63,7 +62,6 @@ protected function execute(InputInterface $input, OutputInterface $output) { $connection = $this->getHelper('jackalope-doctrine-dbal')->getConnection(); if (!$connection instanceof Connection) { - $output->write(PHP_EOL.'The provided connection is not an instance of the Doctrine DBAL connection.'.PHP_EOL); throw new \InvalidArgumentException('The provided connection is not an instance of the Doctrine DBAL connection.'); } @@ -82,7 +80,6 @@ protected function execute(InputInterface $input, OutputInterface $output) if (true === $input->getOption('dump-sql')) { $output->writeln($sql); } else { - $connection->exec($sql); } } @@ -90,6 +87,13 @@ protected function execute(InputInterface $input, OutputInterface $output) if (false === $input->getOption('force')) { throw $e; } + // remove this once we require Doctrine DBAL 2.5+ + } catch (DBALException $e) { + if (false === $input->getOption('force')) { + throw $e; + } else { + $output->writeln($e->getMessage()); + } } } diff --git a/src/Jackalope/Transport/DoctrineDBAL/CachedClient.php b/src/Jackalope/Transport/DoctrineDBAL/CachedClient.php index 0f05bf5b..06ac5d4a 100644 --- a/src/Jackalope/Transport/DoctrineDBAL/CachedClient.php +++ b/src/Jackalope/Transport/DoctrineDBAL/CachedClient.php @@ -4,13 +4,11 @@ use Doctrine\Common\Cache\Cache; use Doctrine\Common\Cache\ArrayCache; - use Doctrine\DBAL\Connection; - use Jackalope\NotImplementedException; use Jackalope\FactoryInterface; use Jackalope\Node; - +use Jackalope\Query\Query; use PHPCR\ItemNotFoundException; /** @@ -36,6 +34,35 @@ public function __construct(FactoryInterface $factory, Connection $conn, array $ $this->caches = $caches; } + /** + * @param array|null $caches which caches to invalidate, null means all except meta + */ + private function clearCaches(array $caches = null) + { + $caches = $caches ?: array('nodes', 'query'); + foreach ($caches as $cache) { + if (isset($this->caches[$cache])) { + $this->caches[$cache]->deleteAll(); + } + } + } + + /** + * @param Node $node + */ + private function clearNodeCache(Node $node) + { + $cacheKey = "nodes: {$node->getPath()}, ".$this->workspaceName; + $this->caches['nodes']->delete($cacheKey); + + // actually in the DBAL all nodes have a uuid .. + if ($node->isNodeType('mix:referenceable')) { + $uuid = $node->getIdentifier(); + $cacheKey = "nodes by uuid: $uuid, ".$this->workspaceName; + $this->caches['nodes']->delete($cacheKey); + } + } + /** * {@inheritDoc} * @@ -58,9 +85,7 @@ public function deleteWorkspace($name) $this->caches['meta']->delete('workspaces'); $this->caches['meta']->delete("workspace: $name"); - if (isset($this->caches['nodes'])) { - $this->caches['nodes']->deleteAll(); - } + $this->clearCaches(); } /** @@ -78,41 +103,60 @@ protected function workspaceExists($workspaceName) return $result; } + /** + * {@inheritDoc} + */ + protected function fetchUserNodeTypes() + { + $cacheKey = 'node_types'; + if (!$this->inTransaction && $result = $this->caches['meta']->fetch($cacheKey)) { + return $result; + } + + $result = parent::fetchUserNodeTypes(); + + if (!$this->inTransaction) { + $this->caches['meta']->save($cacheKey, $result); + } + + return $result; + } + /** * {@inheritDoc} */ public function getNodeTypes($nodeTypes = array()) { $cacheKey = 'nodetypes: '.serialize($nodeTypes); - $nodeTypes = $this->caches['meta']->fetch($cacheKey); - if (!$nodeTypes) { - $nodeTypes = parent::getNodeTypes($nodeTypes); - $this->caches['meta']->save($cacheKey, $nodeTypes); + $result = $this->caches['meta']->fetch($cacheKey); + if (!$result) { + $result = parent::getNodeTypes($nodeTypes); + $this->caches['meta']->save($cacheKey, $result); } - return $nodeTypes; + return $result; } /** - * Return the namespaces of the current session as a referenceable ArrayObject. - * - * @return \ArrayObject + * {@inheritDoc} */ - protected function getNamespacesObject() + public function getNamespaces() { - if ($this->namespaces->count() === 0) { - $cacheKey = 'namespaces'; - $result = $this->caches['meta']->fetch($cacheKey); - if ($result) { - $this->namespaces = $result; - } else { - $this->namespaces = parent::getNamespacesObject(); + if ($this->namespaces instanceof \ArrayObject) { + return parent::getNamespaces(); + } - $this->caches['meta']->save($cacheKey, $this->namespaces); - } + $cacheKey = 'namespaces'; + $result = $this->caches['meta']->fetch($cacheKey); + if ($result) { + $this->setNamespaces($result); + } else { + $result = parent::getNamespaces(); + + $this->caches['meta']->save($cacheKey, $result); } - return $this->namespaces; + return $result; } /** @@ -122,9 +166,7 @@ public function copyNode($srcAbsPath, $dstAbsPath, $srcWorkspace = null) { parent::copyNode($srcAbsPath, $dstAbsPath, $srcWorkspace); - if (isset($this->caches['nodes'])) { - $this->caches['nodes']->deleteAll(); - } + $this->clearCaches(); } /** @@ -147,10 +189,14 @@ public function getAccessibleWorkspaceNames() */ public function getNode($path) { + if (empty($this->caches['nodes'])) { + return parent::getNode($path); + } + $this->assertLoggedIn(); $cacheKey = "nodes: $path, ".$this->workspaceName; - if (isset($this->caches['nodes']) && (false !== ($result = $this->caches['nodes']->fetch($cacheKey)))) { + if (false !== ($result = $this->caches['nodes']->fetch($cacheKey))) { if ('ItemNotFoundException' === $result) { throw new ItemNotFoundException(sprintf('Item "%s" not found in workspace "%s"', $path, $this->workspaceName)); } @@ -168,9 +214,7 @@ public function getNode($path) throw $e; } - if (isset($this->caches['nodes'])) { - $this->caches['nodes']->save($cacheKey, $node); - } + $this->caches['nodes']->save($cacheKey, $node); return $node; } @@ -201,12 +245,16 @@ public function getNodes($paths) */ protected function getSystemIdForNodeUuid($uuid, $workspaceName = null) { + if (empty($this->caches['nodes'])) { + return parent::getSystemIdForNodeUuid($uuid, $workspaceName); + } + if (null === $workspaceName) { $workspaceName = $this->workspaceName; } $cacheKey = "id: $uuid, ".$workspaceName; - if (isset($this->caches['nodes']) && (false !== ($result = $this->caches['nodes']->fetch($cacheKey)))) { + if (false !== ($result = $this->caches['nodes']->fetch($cacheKey))) { if ('false' === $result) { return false; } @@ -215,9 +263,8 @@ protected function getSystemIdForNodeUuid($uuid, $workspaceName = null) } $nodeId = parent::getSystemIdForNodeUuid($uuid, $workspaceName); - if (isset($this->caches['nodes'])) { - $this->caches['nodes']->save($cacheKey, $nodeId ? $nodeId : 'false'); - } + + $this->caches['nodes']->save($cacheKey, $nodeId ? $nodeId : 'false'); return $nodeId; } @@ -259,8 +306,8 @@ public function deleteNodes(array $operations) { $result = parent::deleteNodes($operations); - if ($result && isset($this->caches['nodes'])) { - $this->caches['nodes']->deleteAll(); + if ($result) { + $this->clearCaches(); } return $result; @@ -273,8 +320,9 @@ public function deleteProperties(array $operation) { $result = parent::deleteProperties($operation); - if ($result && isset($this->caches['nodes'])) { - $this->caches['nodes']->deleteAll(); + if ($result) { + // we do not have the node here, otherwise we could use clearNodeCache() and then just invalidate all queries + $this->clearCaches(); } return $result; @@ -287,8 +335,8 @@ public function deleteNodeImmediately($absPath) { $result = parent::deleteNodeImmediately($absPath); - if ($result && isset($this->caches['nodes'])) { - $this->caches['nodes']->deleteAll(); + if ($result) { + $this->clearCaches(); } return $result; @@ -301,8 +349,9 @@ public function deletePropertyImmediately($absPath) { $result = parent::deletePropertyImmediately($absPath); - if ($result && isset($this->caches['nodes'])) { - $this->caches['nodes']->deleteAll(); + if ($result) { + // we do not have the node here, otherwise we could use clearNodeCache() and then just invalidate all queries + $this->clearCaches(); } return $result; @@ -315,8 +364,8 @@ public function moveNodes(array $operations) { $result = parent::moveNodes($operations); - if ($result && isset($this->caches['nodes'])) { - $this->caches['nodes']->deleteAll(); + if ($result) { + $this->clearCaches(); } return $result; @@ -329,8 +378,8 @@ public function moveNodeImmediately($srcAbsPath, $dstAbsPath) { $result = parent::moveNodeImmediately($srcAbsPath, $dstAbsPath); - if ($result && isset($this->caches['nodes'])) { - $this->caches['nodes']->deleteAll(); + if ($result) { + $this->clearCaches(); } return $result; @@ -339,13 +388,15 @@ public function moveNodeImmediately($srcAbsPath, $dstAbsPath) /** * {@inheritDoc} */ - public function reorderNodes($absPath, $reorders) + public function reorderChildren(Node $node) { - parent::reorderNodes($absPath, $reorders); + $result = parent::reorderChildren($node); - if (isset($this->caches['nodes'])) { - $this->caches['nodes']->deleteAll(); + if ($result) { + $this->clearNodeCache($node); } + + return $result; } /** @@ -353,13 +404,10 @@ public function reorderNodes($absPath, $reorders) */ public function storeNodes(array $operations) { - $result = parent::storeNodes($operations); + parent::storeNodes($operations); - if ($result && isset($this->caches['nodes'])) { - $this->caches['nodes']->deleteAll(); - } - - return $result; + // we do not have the node here, otherwise we could just use clearNodeCache() on pre-existing parents and then just invalidate all queries + $this->clearCaches(); } /** @@ -367,14 +415,14 @@ public function storeNodes(array $operations) */ public function getNodePathForIdentifier($uuid, $workspace = null) { - if (null !== $workspace) { - throw new NotImplementedException('Specifying the workspace is not yet supported.'); + if (empty($this->caches['nodes']) || null !== $workspace) { + return parent::getNodePathForIdentifier($uuid); } $this->assertLoggedIn(); $cacheKey = "nodes by uuid: $uuid, ".$this->workspaceName; - if (isset($this->caches['nodes']) && (false !== ($result = $this->caches['nodes']->fetch($cacheKey)))) { + if (false !== ($result = $this->caches['nodes']->fetch($cacheKey))) { if ('ItemNotFoundException' === $result) { throw new ItemNotFoundException("no item found with uuid ".$uuid); } @@ -392,9 +440,7 @@ public function getNodePathForIdentifier($uuid, $workspace = null) throw $e; } - if (isset($this->caches['nodes'])) { - $this->caches['nodes']->save($cacheKey, $path); - } + $this->caches['nodes']->save($cacheKey, $path); return $path; } @@ -417,10 +463,7 @@ public function registerNodeTypes($types, $allowUpdate) public function registerNamespace($prefix, $uri) { parent::registerNamespace($prefix, $uri); - - if (!empty($this->namespaces)) { - $this->caches['meta']->save('namespaces', $this->namespaces); - } + $this->caches['meta']->save('namespaces', $this->namespaces); } /** @@ -429,10 +472,7 @@ public function registerNamespace($prefix, $uri) public function unregisterNamespace($prefix) { parent::unregisterNamespace($prefix); - - if (!empty($this->namespaces)) { - $this->caches['meta']->save('namespaces', $this->namespaces); - } + $this->caches['meta']->save('namespaces', $this->namespaces); } /** @@ -440,16 +480,18 @@ public function unregisterNamespace($prefix) */ public function getReferences($path, $name = null) { - $cacheKey = "nodes references: $path, $name, ".$this->workspaceName; - if (isset($this->caches['nodes']) && (false !== ($result = $this->caches['nodes']->fetch($cacheKey)))) { + if (empty($this->caches['nodes'])) { + return parent::getReferences($path, $name); + } + + $cacheKey = "nodes references: $path, $name, " . $this->workspaceName; + if (false !== ($result = $this->caches['nodes']->fetch($cacheKey))) { return $result; } $references = parent::getReferences($path, $name); - if (isset($this->caches['nodes'])) { - $this->caches['nodes']->save($cacheKey, $references); - } + $this->caches['nodes']->save($cacheKey, $references); return $references; } @@ -459,16 +501,18 @@ public function getReferences($path, $name = null) */ public function getWeakReferences($path, $name = null) { - $cacheKey = "nodes weak references: $path, $name, ".$this->workspaceName; - if (isset($this->caches['nodes']) && $result = $this->caches['nodes']->fetch($cacheKey)) { + if (empty($this->caches['nodes'])) { + return parent::getWeakReferences($path, $name); + } + + $cacheKey = "nodes weak references: $path, $name, " . $this->workspaceName; + if ($result = $this->caches['nodes']->fetch($cacheKey)) { return $result; } $references = parent::getWeakReferences($path, $name); - if (isset($this->caches['nodes'])) { - $this->caches['nodes']->save($cacheKey, $references); - } + $this->caches['nodes']->save($cacheKey, $references); return $references; } @@ -476,14 +520,34 @@ public function getWeakReferences($path, $name = null) /** * {@inheritDoc} */ + public function query(Query $query) + { + if (empty($this->caches['query'])) { + return parent::query($query); + } + + $this->assertLoggedIn(); + + $cacheKey = "query: {$query->getStatement()}, {$query->getLimit()}, {$query->getOffset()}, {$query->getLanguage()}, ".$this->workspaceName; + if ($result = $this->caches['query']->fetch($cacheKey)) { + return $result; + } + + $result = parent::query($query); + + $this->caches['query']->save($cacheKey, $result); + + return $result; + } + + /** + * {@inheritDoc} + */ public function commitTransaction() { parent::commitTransaction(); - $this->caches['meta']->deleteAll(); - if (isset($this->caches['nodes'])) { - $this->caches['nodes']->deleteAll(); - } + $this->clearCaches(array_keys($this->caches)); } /** @@ -493,9 +557,6 @@ public function rollbackTransaction() { parent::rollbackTransaction(); - $this->caches['meta']->deleteAll(); - if (isset($this->caches['nodes'])) { - $this->caches['nodes']->deleteAll(); - } + $this->clearCaches(array_keys($this->caches)); } } diff --git a/src/Jackalope/Transport/DoctrineDBAL/Client.php b/src/Jackalope/Transport/DoctrineDBAL/Client.php index 14597fd8..03185b68 100644 --- a/src/Jackalope/Transport/DoctrineDBAL/Client.php +++ b/src/Jackalope/Transport/DoctrineDBAL/Client.php @@ -25,19 +25,14 @@ use PHPCR\PathNotFoundException; use PHPCR\Query\InvalidQueryException; use PHPCR\NodeType\ConstraintViolationException; - -use PHPCR\UnsupportedRepositoryOperationException; use PHPCR\Util\QOM\Sql2ToQomQueryConverter; use PHPCR\Util\ValueConverter; use PHPCR\Util\UUIDHelper; use PHPCR\Util\PathHelper; - use Doctrine\DBAL\Connection; -use Doctrine\DBAL\Driver\PDOConnection; use Doctrine\DBAL\DBALException; use Doctrine\DBAL\Platforms\PostgreSqlPlatform; use Doctrine\DBAL\Platforms\SqlitePlatform; - use Jackalope\Node; use Jackalope\Property; use Jackalope\Query\Query; @@ -134,14 +129,30 @@ class Client extends BaseTransport implements QueryTransport, WritingInterface, private $checkLoginOnServer = true; /** - * @var \ArrayObject + * Using an ArrayObject here so that we can pass this into the NodeProcessor by reference more elegantly + * + * @var null|\ArrayObject */ protected $namespaces; /** - * @var array The namespaces at initial state, in case of rollback. + * @var null|array The namespaces at initial state when making changes to the namespaces, in case of rollback. + */ + private $originalNamespaces; + + /** + * The core namespaces defined in JCR + * + * @var array */ - private $originalNamespaces = array(); + private $coreNamespaces = array( + NamespaceRegistryInterface::PREFIX_EMPTY => NamespaceRegistryInterface::NAMESPACE_EMPTY, + NamespaceRegistryInterface::PREFIX_JCR => NamespaceRegistryInterface::NAMESPACE_JCR, + NamespaceRegistryInterface::PREFIX_NT => NamespaceRegistryInterface::NAMESPACE_NT, + NamespaceRegistryInterface::PREFIX_MIX => NamespaceRegistryInterface::NAMESPACE_MIX, + NamespaceRegistryInterface::PREFIX_XML => NamespaceRegistryInterface::NAMESPACE_XML, + NamespaceRegistryInterface::PREFIX_SV => NamespaceRegistryInterface::NAMESPACE_SV, + ); /** * @var string|null @@ -195,17 +206,16 @@ public function __construct(FactoryInterface $factory, Connection $conn) $this->factory = $factory; $this->valueConverter = $this->factory->get('PHPCR\Util\ValueConverter'); $this->conn = $conn; - $this->namespaces = new \ArrayObject(); } /** * @TODO: move to "SqlitePlatform" and rename to "registerExtraFunctions"? * - * @param PDOConnection $sqliteConnection + * @param \PDO $sqliteConnection * * @return Client */ - private function registerSqliteFunctions(PDOConnection $sqliteConnection) + private function registerSqliteFunctions(\PDO $sqliteConnection) { $sqliteConnection->sqliteCreateFunction('EXTRACTVALUE', function ($string, $expression) { if (null === $string) { @@ -279,7 +289,7 @@ public function setUuidGenerator(\Closure $generator) protected function getUuidGenerator() { if (!$this->uuidGenerator) { - $this->uuidGenerator = function() { + $this->uuidGenerator = function () { return UUIDHelper::generateUUID(); }; } @@ -513,6 +523,11 @@ public function getRepositoryDescriptors() ); } + /** + * Get the registered namespace prefixes + * + * @return array + */ private function getNamespacePrefixes() { return array_keys($this->getNamespaces()); @@ -533,29 +548,32 @@ public function getNamespaces() */ protected function getNamespacesObject() { - if ($this->namespaces->count() === 0) { - $query = 'SELECT * FROM phpcr_namespaces'; - $data = $this->getConnection()->fetchAll($query); - - $this->namespaces->exchangeArray(array( - NamespaceRegistryInterface::PREFIX_EMPTY => NamespaceRegistryInterface::NAMESPACE_EMPTY, - NamespaceRegistryInterface::PREFIX_JCR => NamespaceRegistryInterface::NAMESPACE_JCR, - NamespaceRegistryInterface::PREFIX_NT => NamespaceRegistryInterface::NAMESPACE_NT, - NamespaceRegistryInterface::PREFIX_MIX => NamespaceRegistryInterface::NAMESPACE_MIX, - NamespaceRegistryInterface::PREFIX_XML => NamespaceRegistryInterface::NAMESPACE_XML, - NamespaceRegistryInterface::PREFIX_SV => NamespaceRegistryInterface::NAMESPACE_SV, - )); - - foreach ($data as $row) { - $this->namespaces[$row['prefix']] = $row['uri']; - } + if (null === $this->namespaces) { + $query = 'SELECT prefix, uri FROM phpcr_namespaces'; + $result = $this->getConnection()->query($query); + $namespaces = (array) $result->fetchAll(\PDO::FETCH_KEY_PAIR); + $namespaces += $this->coreNamespaces; - $this->originalNamespaces = $this->namespaces; + $this->setNamespaces($namespaces); } return $this->namespaces; } + /** + * Set the namespaces property to an \ArrayObject instance + * + * @param array|\ArrayObject $namespaces + */ + protected function setNamespaces($namespaces) + { + if ($this->namespaces instanceof \ArrayObject) { + $this->namespaces->exchangeArray($namespaces); + } else { + $this->namespaces = new \ArrayObject($namespaces); + } + } + /** * Executes an UPDATE on DBAL while ensuring that we never try to send more than 999 parameters to SQLite * @@ -589,16 +607,16 @@ public function copyNode($srcAbsPath, $dstAbsPath, $srcWorkspace = null) PathHelper::assertValidAbsolutePath($dstAbsPath, true, true, $this->getNamespacePrefixes()); - $srcNodeId = $this->getSystemIdForNodePath($srcAbsPath, $srcWorkspace); + $srcNodeId = $this->getSystemIdForNode($srcAbsPath, $srcWorkspace); if (!$srcNodeId) { throw new PathNotFoundException("Source path '$srcAbsPath' not found"); } - if ($this->getSystemIdForNodePath($dstAbsPath)) { + if ($this->getSystemIdForNode($dstAbsPath)) { throw new ItemExistsException("Cannot copy to destination path '$dstAbsPath' that already exists."); } - if (!$this->getSystemIdForNodePath(PathHelper::getParentPath($dstAbsPath))) { + if (!$this->getSystemIdForNode(PathHelper::getParentPath($dstAbsPath))) { throw new PathNotFoundException("Parent of the destination path '" . $dstAbsPath . "' has to exist."); } @@ -684,7 +702,7 @@ public function copyNode($srcAbsPath, $dstAbsPath, $srcWorkspace = null) ' SELECT ?, b.property_name, ?, b.idx, b.data FROM phpcr_binarydata b WHERE b.node_id = ?'; try { - $this->getConnection()->executeUpdate($query, array($newNodeId, $this->workspaceName, $srcNodeId)); + $this->getConnection()->executeUpdate($query, array($newNodeId, $this->workspaceName, $row['id'])); } catch (DBALException $e) { throw new RepositoryException("Unexpected exception while copying node from $srcAbsPath to $dstAbsPath", $e->getCode(), $e); } @@ -727,12 +745,12 @@ private function getJcrName($path) /** * Actually write the node into the database * - * @param string $uuid node uuid - * @param string $path absolute path of the node - * @param string $type node type - * @param boolean $isNewNode new nodes to insert (true) or existing node to update (false) - * @param array $props - * @param array $propsData + * @param string $uuid node uuid + * @param string $path absolute path of the node + * @param string $type node type name + * @param boolean $isNewNode new nodes to insert (true) or existing node to update (false) + * @param Property[] $props + * @param array $propsData * * @return boolean|string * @@ -779,13 +797,13 @@ private function syncNode($uuid, $path, $type, $isNewNode, $props = array(), $pr 'depth' => PathHelper::getPathDepth($path), 'parent_a' => PathHelper::getParentPath($path), )); - } catch(\Exception $e) { + } catch (\Exception $e) { if ($e instanceof \PDOException || $e instanceof DBALException) { - if(strpos($e->getMessage(), "SQLSTATE[23") !== false) { - throw new ItemExistsException('Item ' . $path . ' already exists in the database'); - } else { - throw new RepositoryException('Unknown database error while inserting item ' . $path . ': '.$e->getMessage(), 0, $e); - } + if (strpos($e->getMessage(), "SQLSTATE[23") !== false) { + throw new ItemExistsException('Item ' . $path . ' already exists in the database'); + } else { + throw new RepositoryException('Unknown database error while inserting item ' . $path . ': '.$e->getMessage(), 0, $e); + } } else { throw $e; } @@ -793,7 +811,7 @@ private function syncNode($uuid, $path, $type, $isNewNode, $props = array(), $pr $nodeId = $this->getConnection()->lastInsertId($this->sequenceNodeName); } else { - $nodeId = $this->getSystemIdForNodePath($path); + $nodeId = $this->getSystemIdForNode($path); if (!$nodeId) { throw new RepositoryException("nodeId for $path not found"); } @@ -812,9 +830,7 @@ private function syncNode($uuid, $path, $type, $isNewNode, $props = array(), $pr $this->syncBinaryData($nodeId, $propsData['binaryData']); } - if (!empty($propsData['references'])) { - $this->referencesToUpdate[$nodeId] = array('path' => $path, 'properties' => $propsData['references']); - } + $this->referencesToUpdate[$nodeId] = array('path' => $path, 'properties' => $propsData['references']); return $nodeId; } @@ -845,26 +861,27 @@ private function syncBinaryData($nodeId, $binaryData) } } - private function syncReferences($referencesToUpdate) + private function syncReferences(array $referencesToUpdate) { if ($referencesToUpdate) { // do not update references that are going to be deleted anyways - $toUpdate = array_diff_assoc($referencesToUpdate, $this->referencesToDelete); + $toUpdate = array_diff(array_keys($referencesToUpdate), array_keys($this->referencesToDelete)); try { foreach ($this->referenceTables as $table) { $query = "DELETE FROM $table WHERE source_id IN (?)"; - $this->executeChunkedUpdate($query, array_keys($toUpdate)); + $this->executeChunkedUpdate($query, $toUpdate); } } catch (DBALException $e) { throw new RepositoryException('Unexpected exception while cleaning up after saving', $e->getCode(), $e); } $updates = array(); - foreach ($toUpdate as $nodeId => $references) { + foreach ($toUpdate as $nodeId) { + $references = $referencesToUpdate[$nodeId]; foreach ($references['properties'] as $name => $data) { foreach ($data['values'] as $value) { - $targetId = $this->getSystemIdForNodeUuid($value); + $targetId = $this->getSystemIdForNode($value); if (false === $targetId) { if (PropertyType::REFERENCE === $data['type']) { throw new ReferentialIntegrityException(sprintf( @@ -1008,7 +1025,7 @@ protected function xmlToColumns($xml, $propertyNames) * @param \DOMElement $propertyNode * @param \stdClass $data */ - protected function mapPropertyFromElement(\DOMElement $propertyNode, \stdClass $data) + protected function mapPropertyFromElement(\DOMElement $propertyNode, \stdClass $data) { $name = $propertyNode->getAttribute('sv:name'); @@ -1063,7 +1080,6 @@ protected function mapPropertyFromElement(\DOMElement $propertyNode, \stdClass $ break; } } - /** * Seperate properties array into an xml and binary data. @@ -1095,7 +1111,6 @@ private function propsToXML($properties) $binaryData = $references = array(); foreach ($properties as $property) { - $targetDoms = array('stringDom'); switch ($property->getType()) { @@ -1183,7 +1198,6 @@ private function propsToXML($properties) ); foreach ($doms as $targetDom => $properties) { - $dom = new \DOMDocument('1.0', 'UTF-8'); $rootNode = $dom->createElement('sv:node'); foreach ($namespaces as $namespace => $uri) { @@ -1240,7 +1254,7 @@ public function getNode($path) PathHelper::assertValidAbsolutePath($path, false, true, $this->getNamespacePrefixes()); $values[':path'] = $path; - $values[':pathd'] = rtrim($path,'/') . '/%'; + $values[':pathd'] = rtrim($path, '/') . '/%'; $values[':workspace'] = $this->workspaceName; if ($this->fetchDepth > 0) { @@ -1398,7 +1412,7 @@ public function getNodes($paths) $i = 0; foreach ($paths as $path) { $params[':path'.$i] = $path; - $params[':pathd'.$i] = rtrim($path,'/') . '/%'; + $params[':pathd'.$i] = rtrim($path, '/') . '/%'; $subquery = 'SELECT depth FROM phpcr_nodes WHERE path = :path'.$i.' AND workspace_name = :workspace'; $query .= '(path LIKE :pathd'.$i.' OR path = :path'.$i.') AND depth <= ((' . $subquery . ') + :fetchDepth) OR '; $i++; @@ -1440,52 +1454,34 @@ public function getNodes($paths) */ private function pathExists($path, $workspaceName = null) { - return (boolean) $this->getSystemIdForNodePath($path, $workspaceName); + return (boolean) $this->getSystemIdForNode($path, $workspaceName); } /** - * Get the database primary key for node at path. + * Get the database primary key for node. * - * @param string $path Path of the node + * @param string $identifier Path of the identifier * @param string $workspaceName To overwrite the current workspace * * @return bool|string The database id */ - private function getSystemIdForNodePath($path, $workspaceName = null) + private function getSystemIdForNode($identifier, $workspaceName = null) { if (null === $workspaceName) { $workspaceName = $this->workspaceName; } - if ($this->getConnection()->getDriver() instanceof \Doctrine\DBAL\Driver\PDOMySql\Driver) { - $query = 'SELECT id FROM phpcr_nodes WHERE path COLLATE utf8_bin = ? AND workspace_name = ?'; + if (UUIDHelper::isUUID($identifier)) { + $query = 'SELECT id FROM phpcr_nodes WHERE identifier = ? AND workspace_name = ?'; } else { - $query = 'SELECT id FROM phpcr_nodes WHERE path = ? AND workspace_name = ?'; - } - - if ($nodeId = $this->getConnection()->fetchColumn($query, array($path, $workspaceName))) { - return $nodeId; - } - - return false; - } - - /** - * Get the database primary key for node at path. - * - * @param string $uuid Uuid of the node - * @param string $workspaceName To overwrite the current workspace - * - * @return bool|string The database id - */ - protected function getSystemIdForNodeUuid($uuid, $workspaceName = null) - { - if (null === $workspaceName) { - $workspaceName = $this->workspaceName; + if ($this->getConnection()->getDriver() instanceof \Doctrine\DBAL\Driver\PDOMySql\Driver) { + $query = 'SELECT id FROM phpcr_nodes WHERE path COLLATE utf8_bin = ? AND workspace_name = ?'; + } else { + $query = 'SELECT id FROM phpcr_nodes WHERE path = ? AND workspace_name = ?'; + } } - $query = 'SELECT id FROM phpcr_nodes WHERE identifier = ? AND workspace_name = ?'; - $nodeId = $this->getConnection()->fetchColumn($query, array($uuid, $workspaceName)); + $nodeId = $this->getConnection()->fetchColumn($query, array($identifier, $workspaceName)); return $nodeId ?: false; } @@ -1644,7 +1640,7 @@ protected function deleteNode($path) private function cleanIdentifierCache($root) { unset($this->nodeIdentifiers[$root]); - foreach(array_keys($this->nodeIdentifiers) as $path) { + foreach (array_keys($this->nodeIdentifiers) as $path) { if (strpos($path, "$root/") === 0) { unset($this->nodeIdentifiers[$path]); } @@ -1685,7 +1681,7 @@ protected function deleteProperty($path) $this->assertLoggedIn(); $nodePath = PathHelper::getParentPath($path); - $nodeId = $this->getSystemIdForNodePath($nodePath); + $nodeId = $this->getSystemIdForNode($nodePath); if (!$nodeId) { // no we really don't know that path throw new ItemNotFoundException("No item found at ".$path); @@ -1773,11 +1769,11 @@ protected function moveNode($srcAbsPath, $dstAbsPath) throw new PathNotFoundException("Source path '$srcAbsPath' not found"); } - if ($this->getSystemIdForNodePath($dstAbsPath)) { + if ($this->getSystemIdForNode($dstAbsPath)) { throw new ItemExistsException("Cannot move '$srcAbsPath' to '$dstAbsPath' because destination node already exists."); } - if (!$this->getSystemIdForNodePath(PathHelper::getParentPath($dstAbsPath))) { + if (!$this->getSystemIdForNode(PathHelper::getParentPath($dstAbsPath))) { throw new PathNotFoundException("Parent of the destination path '" . $dstAbsPath . "' has to exist."); } @@ -2043,7 +2039,7 @@ protected function fetchUserNodeTypes() 'multiple' => (bool) $propertyData['multiple'], 'isFulltextSearchable' => (bool) $propertyData['fulltext_searchable'], 'isQueryOrderable' => (bool) $propertyData['query_orderable'], - 'queryOperators' => array ( + 'queryOperators' => array( 0 => 'jcr.operator.equal.to', 1 => 'jcr.operator.not.equal.to', 2 => 'jcr.operator.greater.than', @@ -2179,13 +2175,14 @@ public function updateNode(Node $node, $srcWorkspace) /** * {@inheritDoc} + * @throws RepositoryException when no binary data found */ public function getBinaryStream($path) { $this->assertLoggedIn(); $nodePath = PathHelper::getParentPath($path); - $nodeId = $this->getSystemIdForNodePath($nodePath); + $nodeId = $this->getSystemIdForNode($nodePath); $propertyName = PathHelper::getNodeName($path); $data = $this->getConnection()->fetchAll( @@ -2193,6 +2190,10 @@ public function getBinaryStream($path) array($nodeId, $propertyName, $this->workspaceName) ); + if (count($data) === 0) { + throw new RepositoryException('No binary data found in stream'); + } + $streams = array(); foreach ($data as $row) { if (is_resource($row['data'])) { @@ -2206,13 +2207,18 @@ public function getBinaryStream($path) $streams[] = $stream; } - // TODO even a multi value field could have only one value stored - // we need to also fetch if the property is multi valued instead of this count() check - if (count($data) > 1) { - return $streams; + if (count($data) === 1) { + // we don't know if this is a multivalue property or not. + // TODO we should have something more efficient to know this. a flag in the database? + + // TODO use self::getProperty()->isMultiple() once implemented + $node = $this->getNode($nodePath); + if (!is_array($node->{':'.$propertyName})) { + return reset($streams); + } } - return reset($streams); + return $streams; } /** @@ -2220,7 +2226,7 @@ public function getBinaryStream($path) */ public function getProperty($path) { - throw new NotImplementedException('Getting properties by path is implemented yet'); + throw new NotImplementedException('Getting properties by path is not implemented yet'); } /** @@ -2363,15 +2369,45 @@ public function getSupportedQueryLanguages() ); } + /** + * We need to create an in memory backup when we are inside a transaction + * so that we can efficiently restore the original state in the namespaces + * property in case of a rollback. + * + * This method also ensures that namespaces are loaded to begin with. + */ + private function ensureNamespacesBackup() + { + if (!$this->namespaces instanceof \ArrayObject) { + $this->getNamespacesObject(); + } + + if (!$this->inTransaction) { + return; + } + + if (null === $this->originalNamespaces) { + $this->originalNamespaces = $this->namespaces->getArrayCopy(); + } + } + /** * {@inheritDoc} */ public function registerNamespace($prefix, $uri) { - if (isset($this->namespaces[$prefix]) && $this->namespaces[$prefix] === $uri) { - return; + if (isset($this->namespaces[$prefix])) { + if ($this->namespaces[$prefix] === $uri) { + return; + } + + if (isset($this->coreNamespaces[$prefix])) { + throw new NamespaceException("Cannot overwrite JCR core namespace prefix '$prefix' to a new uri '$uri'."); + } } + $this->ensureNamespacesBackup(); + $this->getConnection()->delete('phpcr_namespaces', array('prefix' => $prefix)); $this->getConnection()->delete('phpcr_namespaces', array('uri' => $uri)); @@ -2390,6 +2426,12 @@ public function registerNamespace($prefix, $uri) */ public function unregisterNamespace($prefix) { + if (isset($this->coreNamespaces[$prefix])) { + throw new NamespaceException("Cannot unregister JCR core namespace prefix '$prefix'."); + } + + $this->ensureNamespacesBackup(); + $this->getConnection()->delete('phpcr_namespaces', array('prefix' => $prefix)); if (!empty($this->namespaces)) { @@ -2422,7 +2464,7 @@ public function getWeakReferences($path, $name = null) */ private function getNodeReferences($path, $name = null, $weakReference = false) { - $targetId = $this->getSystemIdForNodePath($path); + $targetId = $this->getSystemIdForNode($path); $params = array($targetId); $table = $weakReference ? $this->referenceTables[PropertyType::WEAKREFERENCE] : $this->referenceTables[PropertyType::REFERENCE]; @@ -2473,8 +2515,11 @@ public function commitTransaction() $this->inTransaction = false; $this->getConnection()->commit(); - // now that the transaction is committed, update the cache of the stored namespaces. - $this->originalNamespaces = (array) $this->namespaces; + + if ($this->originalNamespaces) { + // now that the transaction is committed, reset the cache of the stored namespaces. + $this->originalNamespaces = null; + } } catch (\Exception $e) { throw new RepositoryException('Commit transaction failed: ' . $e->getMessage()); } @@ -2494,10 +2539,13 @@ public function rollbackTransaction() try { $this->inTransaction = false; - // reset namespaces - $this->namespaces->exchangeArray($this->originalNamespaces); - $this->getConnection()->rollback(); + + if ($this->originalNamespaces) { + // reset namespaces + $this->setNamespaces($this->originalNamespaces); + $this->originalNamespaces = null; + } } catch (\Exception $e) { throw new RepositoryException('Rollback transaction failed: ' . $e->getMessage(), 0, $e); } diff --git a/src/Jackalope/Transport/DoctrineDBAL/LoggingClient.php b/src/Jackalope/Transport/DoctrineDBAL/LoggingClient.php index b40b3706..612b54c8 100644 --- a/src/Jackalope/Transport/DoctrineDBAL/LoggingClient.php +++ b/src/Jackalope/Transport/DoctrineDBAL/LoggingClient.php @@ -4,7 +4,6 @@ use Jackalope\FactoryInterface; use Jackalope\Transport\AbstractReadWriteLoggingWrapper; - use Jackalope\Transport\QueryInterface as QueryTransport; use Jackalope\Transport\PermissionInterface; use Jackalope\Transport\TransactionInterface; @@ -13,11 +12,9 @@ use Jackalope\Transport\LockingInterface; use Jackalope\Transport\ObservationInterface; use Jackalope\Transport\WorkspaceManagementInterface; - use Jackalope\Query\Query; use PHPCR\Observation\EventFilterInterface; use PHPCR\SessionInterface; - use Jackalope\Transport\Logging\LoggerInterface; /** diff --git a/src/Jackalope/Transport/DoctrineDBAL/Query/QOMWalker.php b/src/Jackalope/Transport/DoctrineDBAL/Query/QOMWalker.php index 64d4cd37..667737d4 100644 --- a/src/Jackalope/Transport/DoctrineDBAL/Query/QOMWalker.php +++ b/src/Jackalope/Transport/DoctrineDBAL/Query/QOMWalker.php @@ -7,12 +7,10 @@ use Jackalope\Query\QOM\QueryObjectModel; use Jackalope\Transport\DoctrineDBAL\RepositorySchema; use Jackalope\Transport\DoctrineDBAL\Util\Xpath; - use PHPCR\NamespaceException; use PHPCR\NodeType\NodeTypeManagerInterface; use PHPCR\Query\InvalidQueryException; use PHPCR\Query\QOM; - use Doctrine\DBAL\Connection; use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Platforms\MySqlPlatform; @@ -537,17 +535,17 @@ public function walkComparisonConstraint(QOM\ComparisonInterface $constraint) // Check if we have a property and a literal value (in random order) if ( - ($operator1 instanceOf QOM\PropertyValueInterface - && $operator2 instanceOf QOM\LiteralInterface) - || ($operator1 instanceOf QOM\LiteralInterface - && $operator2 instanceOf QOM\PropertyValueInterface) - || ($operator1 instanceOf QOM\NodeNameInterface - && $operator2 instanceOf QOM\LiteralInterface) - || ($operator1 instanceOf QOM\LiteralInterface - && $operator2 instanceOf QOM\NodeNameInterface) + ($operator1 instanceof QOM\PropertyValueInterface + && $operator2 instanceof QOM\LiteralInterface) + || ($operator1 instanceof QOM\LiteralInterface + && $operator2 instanceof QOM\PropertyValueInterface) + || ($operator1 instanceof QOM\NodeNameInterface + && $operator2 instanceof QOM\LiteralInterface) + || ($operator1 instanceof QOM\LiteralInterface + && $operator2 instanceof QOM\NodeNameInterface) ) { // Check whether the left is the literal, at this point the other always is the literal/nodename operand - if ($operator1 instanceOf QOM\LiteralInterface) { + if ($operator1 instanceof QOM\LiteralInterface) { $operand = $operator2; $literalOperand = $operator1; } else { @@ -763,8 +761,9 @@ public function walkOrdering(QOM\OrderingInterface $ordering) $numericalSelector = $this->sqlXpathExtractValue($alias, $property, 'numerical_props'); - $sql = sprintf('CAST(%s AS DECIMAL), %s', + $sql = sprintf('CAST(%s AS DECIMAL) %s, %s', $numericalSelector, + $direction, $sql ); } diff --git a/src/Jackalope/Transport/DoctrineDBAL/RepositorySchema.php b/src/Jackalope/Transport/DoctrineDBAL/RepositorySchema.php index 657cb753..5fc289cc 100644 --- a/src/Jackalope/Transport/DoctrineDBAL/RepositorySchema.php +++ b/src/Jackalope/Transport/DoctrineDBAL/RepositorySchema.php @@ -186,7 +186,6 @@ protected function addTypePropsTable() $propTypes->addColumn('query_operators', 'integer'); // BITMASK $propTypes->addColumn('default_value', 'string', array('notnull' => false)); $propTypes->setPrimaryKey(array('node_type_id', 'name')); - } protected function addTypeChildsTable() @@ -218,5 +217,4 @@ public function reset() $this->connection->exec($sql); } } - } diff --git a/src/Jackalope/Transport/DoctrineDBAL/Util/Xpath.php b/src/Jackalope/Transport/DoctrineDBAL/Util/Xpath.php index 52ec1268..2b570f25 100644 --- a/src/Jackalope/Transport/DoctrineDBAL/Util/Xpath.php +++ b/src/Jackalope/Transport/DoctrineDBAL/Util/Xpath.php @@ -10,7 +10,6 @@ */ class Xpath { - /** * @param $query * @@ -55,7 +54,6 @@ public static function escape($query, $enclosure = '"', $doubleEscapeSingleQuote $current = ''; foreach (str_split($query) as $character) { - if (in_array($character, $quotechars)) { if ('' !== $current) { $parts[] = $enclosure . $current . $enclosure; @@ -71,7 +69,6 @@ public static function escape($query, $enclosure = '"', $doubleEscapeSingleQuote } else { $current .= $character; } - } if ($current) { @@ -109,5 +106,4 @@ public static function concatBy2(array $parts) return 'concat(' . $part1 . ', ' . self::concatBy2($parts) . ')'; } - } diff --git a/tests/ImplementationLoader.php b/tests/ImplementationLoader.php index 9da991c8..82c1cca8 100644 --- a/tests/ImplementationLoader.php +++ b/tests/ImplementationLoader.php @@ -191,5 +191,4 @@ public function getFixtureLoader() $this->fixturePath ); } - } diff --git a/tests/Jackalope/Test/Fixture/DBUnitFixtureXML.php b/tests/Jackalope/Test/Fixture/DBUnitFixtureXML.php index c8330d9b..ede57cac 100644 --- a/tests/Jackalope/Test/Fixture/DBUnitFixtureXML.php +++ b/tests/Jackalope/Test/Fixture/DBUnitFixtureXML.php @@ -132,31 +132,31 @@ public function addRootNode($workspaceName = 'default') $this->ids[$uuid] = self::$idCounter++; return $this->addRow('phpcr_nodes', array( - 'id' => $this->ids[$uuid], - 'path' => '/', - 'parent' => '', - 'local_name' => '', - 'namespace' => '', - 'workspace_name' => $workspaceName, - 'identifier' => $uuid, - 'type' => 'nt:unstructured', - 'props' => '' - . '', - 'depth' => 0, - 'sort_order' => 0, + 'id' => $this->ids[$uuid], + 'path' => '/', + 'parent' => '', + 'local_name' => '', + 'namespace' => '', + 'workspace_name'=> $workspaceName, + 'identifier' => $uuid, + 'type' => 'nt:unstructured', + 'props' => '' + . '', + 'depth' => 0, + 'sort_order' => 0, )); } @@ -252,7 +252,7 @@ public function getAttributes(\DOMElement $node) foreach ($node->childNodes as $child) { if ($child instanceof \DOMElement && $child->tagName == 'sv:property') { - list ($name, $propertyNameibute) = $this->getChildAttribute($child); + list($name, $propertyNameibute) = $this->getChildAttribute($child); $properties[$name] = $propertyNameibute; } } diff --git a/tests/Jackalope/Test/Fixture/JCRSystemXML.php b/tests/Jackalope/Test/Fixture/JCRSystemXML.php index bf0d7561..47cc5850 100644 --- a/tests/Jackalope/Test/Fixture/JCRSystemXML.php +++ b/tests/Jackalope/Test/Fixture/JCRSystemXML.php @@ -36,5 +36,4 @@ public function getNodes() { return $this->getElementsByTagNameNS($this->namespaces['sv'], 'node'); } - } diff --git a/tests/Jackalope/Test/Fixture/XMLDocument.php b/tests/Jackalope/Test/Fixture/XMLDocument.php index e365caba..d7b94b77 100644 --- a/tests/Jackalope/Test/Fixture/XMLDocument.php +++ b/tests/Jackalope/Test/Fixture/XMLDocument.php @@ -128,5 +128,4 @@ public function save($file = null) return $this; } - } diff --git a/tests/Jackalope/Test/TestCase.php b/tests/Jackalope/Test/TestCase.php index c63ad77c..b4cdce32 100644 --- a/tests/Jackalope/Test/TestCase.php +++ b/tests/Jackalope/Test/TestCase.php @@ -4,8 +4,9 @@ use Doctrine\DBAL\Connection; use Doctrine\DBAL\DriverManager; +use Jackalope\TestCase as BaseTestCase; -abstract class TestCase extends \Jackalope\TestCase +abstract class TestCase extends BaseTestCase { /** * @var Connection diff --git a/tests/Jackalope/Test/Tester/Generic.php b/tests/Jackalope/Test/Tester/Generic.php index 45539c2e..b243acd8 100644 --- a/tests/Jackalope/Test/Tester/Generic.php +++ b/tests/Jackalope/Test/Tester/Generic.php @@ -76,5 +76,4 @@ public function import($fixtureName, $workspace = null) $this->onSetUp(); } - } diff --git a/tests/Jackalope/Test/Tester/Mysql.php b/tests/Jackalope/Test/Tester/Mysql.php index 3086b884..778e082a 100644 --- a/tests/Jackalope/Test/Tester/Mysql.php +++ b/tests/Jackalope/Test/Tester/Mysql.php @@ -18,5 +18,4 @@ public function onSetUp() $this->getConnection()->getConnection()->exec('SET foreign_key_checks = 1'); } - } diff --git a/tests/Jackalope/Test/Tester/Pgsql.php b/tests/Jackalope/Test/Tester/Pgsql.php index ca740b13..8f888638 100644 --- a/tests/Jackalope/Test/Tester/Pgsql.php +++ b/tests/Jackalope/Test/Tester/Pgsql.php @@ -27,5 +27,4 @@ public function onSetUp() $pdo->query($query); } } - } diff --git a/tests/Jackalope/Tools/Console/InitDoctrineDbalCommandTest.php b/tests/Jackalope/Tools/Console/InitDoctrineDbalCommandTest.php index 1fa57860..a7d4feb0 100644 --- a/tests/Jackalope/Tools/Console/InitDoctrineDbalCommandTest.php +++ b/tests/Jackalope/Tools/Console/InitDoctrineDbalCommandTest.php @@ -105,4 +105,4 @@ public function __construct($msg, $code) $this->message = $msg; $this->code = $code; } -} \ No newline at end of file +} diff --git a/tests/Jackalope/Transport/DoctrineDBAL/ClientTest.php b/tests/Jackalope/Transport/DoctrineDBAL/ClientTest.php index fb73bbcf..ef017cad 100644 --- a/tests/Jackalope/Transport/DoctrineDBAL/ClientTest.php +++ b/tests/Jackalope/Transport/DoctrineDBAL/ClientTest.php @@ -71,7 +71,7 @@ public function testReorderNodes() $this->session->save(); $topic->orderBefore('page3', 'page1'); - $topic->orderBefore('page4', NULL); + $topic->orderBefore('page4', null); $this->session->save(); @@ -284,7 +284,6 @@ public function testPropertyLengthAttribute() /** @var $value \DOMElement */ foreach ($values as $index => $value) { - $lengthAttribute = $value->attributes->getNamedItem('length'); if (null === $lengthAttribute) { $this->fail(sprintf('Value %d for property "%s" is expected to have an attribute "length"', $index, $propertyName)); diff --git a/tests/Jackalope/Transport/DoctrineDBAL/Query/QOMWalkerTest.php b/tests/Jackalope/Transport/DoctrineDBAL/Query/QOMWalkerTest.php index 343adb79..ce79c6e3 100644 --- a/tests/Jackalope/Transport/DoctrineDBAL/Query/QOMWalkerTest.php +++ b/tests/Jackalope/Transport/DoctrineDBAL/Query/QOMWalkerTest.php @@ -7,7 +7,6 @@ use Jackalope\Query\QOM\PropertyValue; use Jackalope\Query\QOM\QueryObjectModelFactory; use Jackalope\Factory; - use PHPCR\Query\QOM\QueryObjectModelConstantsInterface; class QOMWalkerTest extends TestCase @@ -45,7 +44,7 @@ public function setUp() public function testDefaultQuery() { - $this->nodeTypeManager->expects($this->once())->method('getSubtypes')->will($this->returnValue( array() )); + $this->nodeTypeManager->expects($this->once())->method('getSubtypes')->will($this->returnValue(array())); $query = $this->factory->createQuery($this->factory->selector('nt:unstructured', 'nt:unstructured'), null, array(), array()); list($selectors, $selectorAliases, $sql) = $this->walker->walkQOMQuery($query); @@ -55,7 +54,7 @@ public function testDefaultQuery() public function testQueryWithPathComparisonConstraint() { - $this->nodeTypeManager->expects($this->once())->method('getSubtypes')->will($this->returnValue( array() )); + $this->nodeTypeManager->expects($this->once())->method('getSubtypes')->will($this->returnValue(array())); $query = $this->factory->createQuery( $this->factory->selector('nt:unstructured', 'nt:unstructured'), @@ -70,7 +69,7 @@ public function testQueryWithPathComparisonConstraint() public function testQueryWithPropertyComparisonConstraint() { - $this->nodeTypeManager->expects($this->once())->method('getSubtypes')->will($this->returnValue( array() )); + $this->nodeTypeManager->expects($this->once())->method('getSubtypes')->will($this->returnValue(array())); $query = $this->factory->createQuery( $this->factory->selector('nt:unstructured', 'nt:unstructured'), @@ -87,7 +86,7 @@ public function testQueryWithPropertyComparisonConstraint() public function testQueryWithPropertyComparisonConstraintNumericLiteral() { - $this->nodeTypeManager->expects($this->once())->method('getSubtypes')->will($this->returnValue( array() )); + $this->nodeTypeManager->expects($this->once())->method('getSubtypes')->will($this->returnValue(array())); $query = $this->factory->createQuery( $this->factory->selector('a', 'nt:unstructured'), @@ -102,7 +101,7 @@ public function testQueryWithPropertyComparisonConstraintNumericLiteral() public function testQueryWithAndConstraint() { - $this->nodeTypeManager->expects($this->once())->method('getSubtypes')->will($this->returnValue( array() )); + $this->nodeTypeManager->expects($this->once())->method('getSubtypes')->will($this->returnValue(array())); $query = $this->factory->createQuery( $this->factory->selector('nt:unstructured', 'nt:unstructured'), @@ -120,7 +119,7 @@ public function testQueryWithAndConstraint() public function testQueryWithOrConstraint() { - $this->nodeTypeManager->expects($this->once())->method('getSubtypes')->will($this->returnValue( array() )); + $this->nodeTypeManager->expects($this->once())->method('getSubtypes')->will($this->returnValue(array())); $query = $this->factory->createQuery( $this->factory->selector('nt:unstructured', 'nt:unstructured'), @@ -138,7 +137,7 @@ public function testQueryWithOrConstraint() public function testQueryWithNotConstraint() { - $this->nodeTypeManager->expects($this->once())->method('getSubtypes')->will($this->returnValue( array() )); + $this->nodeTypeManager->expects($this->once())->method('getSubtypes')->will($this->returnValue(array())); $query = $this->factory->createQuery( $this->factory->selector('nt:unstructured', 'nt:unstructured'), @@ -173,7 +172,7 @@ public static function dataQueryWithOperator() */ public function testQueryWithOperator($const, $op) { - $this->nodeTypeManager->expects($this->once())->method('getSubtypes')->will($this->returnValue( array() )); + $this->nodeTypeManager->expects($this->once())->method('getSubtypes')->will($this->returnValue(array())); $query = $this->factory->createQuery( $this->factory->selector('nt:unstructured', 'nt:unstructured'), @@ -188,7 +187,7 @@ public function testQueryWithOperator($const, $op) public function testQueryWithPathOrder() { - $this->nodeTypeManager->expects($this->once())->method('getSubtypes')->will($this->returnValue( array() )); + $this->nodeTypeManager->expects($this->once())->method('getSubtypes')->will($this->returnValue(array())); $query = $this->factory->createQuery( $this->factory->selector('nt:unstructured', 'nt:unstructured'), @@ -209,7 +208,7 @@ public function testQueryWithOrderings() { $driver = $this->conn->getDriver()->getName(); - $this->nodeTypeManager->expects($this->once())->method('getSubtypes')->will($this->returnValue( array() )); + $this->nodeTypeManager->expects($this->once())->method('getSubtypes')->will($this->returnValue(array())); $query = $this->factory->createQuery( $this->factory->selector('nt:unstructured', 'nt:unstructured'), @@ -223,13 +222,13 @@ public function testQueryWithOrderings() switch ($driver) { case 'pdo_pgsql': - $ordering = - "CAST((xpath('//sv:property[@sv:name=\"foobar\"]/sv:value[1]/text()', CAST(n0.numerical_props AS xml), ARRAY[ARRAY['sv', 'http://www.jcp.org/jcr/sv/1.0']]))[1]::text AS DECIMAL), " . + $ordering = + "CAST((xpath('//sv:property[@sv:name=\"foobar\"]/sv:value[1]/text()', CAST(n0.numerical_props AS xml), ARRAY[ARRAY['sv', 'http://www.jcp.org/jcr/sv/1.0']]))[1]::text AS DECIMAL) ASC, " . "(xpath('//sv:property[@sv:name=\"foobar\"]/sv:value[1]/text()', CAST(n0.props AS xml), ARRAY[ARRAY['sv', 'http://www.jcp.org/jcr/sv/1.0']]))[1]::text ASC"; break; default: - $ordering = - "CAST(EXTRACTVALUE(n0.numerical_props, '//sv:property[@sv:name=\"foobar\"]/sv:value[1]') AS DECIMAL), " . + $ordering = + "CAST(EXTRACTVALUE(n0.numerical_props, '//sv:property[@sv:name=\"foobar\"]/sv:value[1]') AS DECIMAL) ASC, " . "EXTRACTVALUE(n0.props, '//sv:property[@sv:name=\"foobar\"]/sv:value[1]') ASC"; } @@ -250,7 +249,7 @@ public function testQueryWithOrderings() public function testDescendantQuery() { - $this->nodeTypeManager->expects($this->exactly(2))->method('getSubtypes')->will($this->returnValue( array() )); + $this->nodeTypeManager->expects($this->exactly(2))->method('getSubtypes')->will($this->returnValue(array())); $query = $this->factory->createQuery( $this->factory->selector('nt:unstructured', 'nt:unstructured'), @@ -288,7 +287,7 @@ public function testWalkOperand() */ public function testDescendantQueryTrailingSlash() { - $this->nodeTypeManager->expects($this->once())->method('getSubtypes')->will($this->returnValue( array() )); + $this->nodeTypeManager->expects($this->once())->method('getSubtypes')->will($this->returnValue(array())); $query = $this->factory->createQuery( $this->factory->selector('nt:unstructured', 'nt:unstructured'), $this->factory->descendantNode('nt:unstructured', '/some/node/') diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 630d1d99..06a5c374 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -1,7 +1,7 @@ add('Jackalope\\Test', __DIR__); +/** Make Jackalope base tests autoloadable */ $loader->add('Jackalope', __DIR__ . '/../vendor/jackalope/jackalope/tests'); -// @TODO: move FixtureLoaderInterface to some autoload-able path -require_once __DIR__ . '/../vendor/phpcr/phpcr-api-tests/inc/FixtureLoaderInterface.php'; -// @TODO: change phpcr-api-tests/inc/BaseCase to use namespaced ImplementationLoader -require_once __DIR__ . '/../vendor/phpcr/phpcr-api-tests/inc/AbstractLoader.php'; +/** Load the implementation loader class */ require_once __DIR__ . '/ImplementationLoader.php'; require_once __DIR__ . '/generate_fixtures.php'; -// generate fixtures +/** generate fixtures */ generate_fixtures( __DIR__ . '/../vendor/phpcr/phpcr-api-tests/fixtures', __DIR__ . '/fixtures/doctrine' ); -// set up the backend connection -// For further details, please see Doctrine configuration page. -// http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connection-details +/** + * set up the backend connection + * For further details, please see Doctrine configuration page. + * http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connection-details + */ $dbConn = \Doctrine\DBAL\DriverManager::getConnection(array( 'driver' => @$GLOBALS['phpcr.doctrine.dbal.driver'], 'path' => @$GLOBALS['phpcr.doctrine.dbal.path'], @@ -39,23 +38,24 @@ 'dbname' => @$GLOBALS['phpcr.doctrine.dbal.dbname'] )); -// recreate database schema +/** Recreate database schema */ if (!getenv('JACKALOPE_NO_TEST_DB_INIT')) { $options = array('disable_fks' => $dbConn->getDatabasePlatform() instanceof \Doctrine\DBAL\Platforms\SqlitePlatform); $repositorySchema = new \Jackalope\Transport\DoctrineDBAL\RepositorySchema($options, $dbConn); $repositorySchema->reset(); } -// some constants -define('SPEC_VERSION_DESC', 'jcr.specification.version'); -define('SPEC_NAME_DESC', 'jcr.specification.name'); -define('REP_VENDOR_DESC', 'jcr.repository.vendor'); -define('REP_VENDOR_URL_DESC', 'jcr.repository.vendor.url'); -define('REP_NAME_DESC', 'jcr.repository.name'); -define('REP_VERSION_DESC', 'jcr.repository.version'); -define('OPTION_TRANSACTIONS_SUPPORTED', 'option.transactions.supported'); -define('OPTION_VERSIONING_SUPPORTED', 'option.versioning.supported'); -define('OPTION_OBSERVATION_SUPPORTED', 'option.observation.supported'); -define('OPTION_LOCKING_SUPPORTED', 'option.locking.supported'); +/** + * constants for the repository descriptor test for JCR 1.0/JSR-170 and JSR-283 specs + */ -//@TODO: do not pollute global space +define('SPEC_VERSION_DESC', 'jcr.specification.version'); +define('SPEC_NAME_DESC', 'jcr.specification.name'); +define('REP_VENDOR_DESC', 'jcr.repository.vendor'); +define('REP_VENDOR_URL_DESC', 'jcr.repository.vendor.url'); +define('REP_NAME_DESC', 'jcr.repository.name'); +define('REP_VERSION_DESC', 'jcr.repository.version'); +define('OPTION_TRANSACTIONS_SUPPORTED', 'option.transactions.supported'); +define('OPTION_VERSIONING_SUPPORTED', 'option.versioning.supported'); +define('OPTION_OBSERVATION_SUPPORTED', 'option.observation.supported'); +define('OPTION_LOCKING_SUPPORTED', 'option.locking.supported');