diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 00000000..050d624e
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,9 @@
+.gitattributes export-ignore
+/.github/ export-ignore
+.gitignore export-ignore
+/.php_cs.dist export-ignore
+/phpstan.neon.dist export-ignore
+/phpstan.tests.neon.dist export-ignore
+/phpunit.xml.dist export-ignore
+/stubs/ export-ignore
+/tests/ export-ignore
diff --git a/.github/workflows/static.yml b/.github/workflows/static.yml
new file mode 100644
index 00000000..b1403f2f
--- /dev/null
+++ b/.github/workflows/static.yml
@@ -0,0 +1,42 @@
+name: Static analysis
+
+on:
+ push:
+ branches:
+ - '[0-9]+.x'
+ - '[0-9]+.[0-9]+'
+ - '[0-9]+.[0-9]+.x'
+ pull_request:
+
+jobs:
+ phpstan-src:
+ name: PHPStan src
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v2
+
+ - name: PHPStan
+ uses: docker://oskarstark/phpstan-ga
+ with:
+ args: analyze --no-progress
+
+ phpstan-tests:
+ name: PHPStan tests
+ runs-on: ubuntu-latest
+ env:
+ REQUIRE_DEV: "true"
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v2
+
+ - name: Install dependencies
+ run: |
+ composer update --no-progress
+
+ - name: PHPStan
+ uses: docker://oskarstark/phpstan-ga
+ with:
+ args: analyze --no-progress -c phpstan.tests.neon.dist
diff --git a/.github/workflows/test-application.yaml b/.github/workflows/test-application.yaml
index 5c82c002..760386b7 100644
--- a/.github/workflows/test-application.yaml
+++ b/.github/workflows/test-application.yaml
@@ -18,15 +18,15 @@ jobs:
fail-fast: false
matrix:
include:
- - php-version: '7.1'
- dependencies: 'lowest'
- php-version: '7.2'
+ dependencies: 'lowest'
- php-version: '7.3'
- php-version: '7.4'
- php-version: '8.0'
- php-version: '8.0'
dev-dependencies: true
- php-version: '8.1'
+ - php-version: '8.2'
steps:
- name: Checkout project
diff --git a/composer.json b/composer.json
index fda2633a..3364e456 100644
--- a/composer.json
+++ b/composer.json
@@ -27,13 +27,14 @@
}
],
"require": {
- "php": "^7.1 || ^8.0",
+ "php": "^7.2 || ^8.0",
"phpcr/phpcr": "~2.1.0",
"symfony/console": "^2.3 || ^3.0 || ^4.0 || ^5.0 || ^6.0"
},
"require-dev": {
"ramsey/uuid": "^3.5",
- "phpunit/phpunit": "^7.5 || ^8.0 || ^9.0"
+ "phpunit/phpunit": "^7.5 || ^8.0 || ^9.0",
+ "phpstan/phpstan": "^1.9"
},
"suggest": {
"ramsey/uuid": "A library for generating RFC 4122 version 1, 3, 4, and 5 universally unique identifiers (UUID)."
diff --git a/phpstan.neon.dist b/phpstan.neon.dist
new file mode 100644
index 00000000..bc5b3efe
--- /dev/null
+++ b/phpstan.neon.dist
@@ -0,0 +1,9 @@
+parameters:
+ level: 2
+ paths:
+ - src
+ ignoreErrors:
+ -
+ message: "#Symfony\\\\Component\\\\Console\\\\Helper\\\\DialogHelper#"
+ count: 3
+ path: src/PHPCR/Util/Console/Command/BaseCommand.php
diff --git a/phpstan.tests.neon.dist b/phpstan.tests.neon.dist
new file mode 100644
index 00000000..0efe94d4
--- /dev/null
+++ b/phpstan.tests.neon.dist
@@ -0,0 +1,4 @@
+parameters:
+ level: 1
+ paths:
+ - tests
diff --git a/src/PHPCR/Util/CND/Parser/CndParser.php b/src/PHPCR/Util/CND/Parser/CndParser.php
index 0a18429f..dab5dc9e 100644
--- a/src/PHPCR/Util/CND/Parser/CndParser.php
+++ b/src/PHPCR/Util/CND/Parser/CndParser.php
@@ -733,11 +733,6 @@ protected function parseQueryOpsAttribute()
$ops[] = $op;
} while ($op && $this->checkAndExpectToken(Token::TK_SYMBOL, ','));
- if (empty($ops)) {
- // There must be at least an operator if this attribute is not variant
- throw new ParserException($this->tokenQueue, 'Operator expected');
- }
-
return $ops;
}
diff --git a/src/PHPCR/Util/CND/Reader/FileReader.php b/src/PHPCR/Util/CND/Reader/FileReader.php
index 2115b5ff..353594d3 100644
--- a/src/PHPCR/Util/CND/Reader/FileReader.php
+++ b/src/PHPCR/Util/CND/Reader/FileReader.php
@@ -13,7 +13,7 @@ class FileReader extends BufferReader
/**
* @var string
*/
- protected $filePath;
+ protected $path;
/**
* @param string $path
diff --git a/src/PHPCR/Util/CND/Writer/CndWriter.php b/src/PHPCR/Util/CND/Writer/CndWriter.php
index 4df28c75..2ab75382 100644
--- a/src/PHPCR/Util/CND/Writer/CndWriter.php
+++ b/src/PHPCR/Util/CND/Writer/CndWriter.php
@@ -5,7 +5,6 @@
use PHPCR\NamespaceRegistryInterface;
use PHPCR\NodeType\NodeDefinitionInterface;
use PHPCR\NodeType\NodeTypeDefinitionInterface;
-use PHPCR\NodeType\NodeTypeManagerInterface;
use PHPCR\NodeType\NodeTypeTemplateInterface;
use PHPCR\NodeType\PropertyDefinitionInterface;
use PHPCR\PropertyType;
@@ -34,9 +33,6 @@ class CndWriter
/** @var array hashmap of prefix => namespace uri */
private $namespaces = [];
- /**
- * @param NodeTypeManagerInterface $ntm
- */
public function __construct(NamespaceRegistryInterface $ns)
{
$this->ns = $ns;
@@ -135,6 +131,11 @@ protected function writeNodeType(NodeTypeDefinitionInterface $nodeType)
return $s;
}
+ /**
+ * @param PropertyDefinitionInterface[] $properties
+ *
+ * @return string
+ */
private function writeProperties($properties)
{
if (null === $properties) {
@@ -145,7 +146,6 @@ private function writeProperties($properties)
$s = '';
- /** @var $property PropertyDefinitionInterface */
foreach ($properties as $property) {
$this->checkNamespace($property->getName());
$s .= '- '.$property->getName();
@@ -196,6 +196,11 @@ private function writeProperties($properties)
return $s;
}
+ /**
+ * @param NodeDefinitionInterface[] $children
+ *
+ * @return string
+ */
private function writeChildren($children)
{
if (null === $children) {
@@ -206,7 +211,6 @@ private function writeChildren($children)
$s = '';
- /** @var $child NodeDefinitionInterface */
foreach ($children as $child) {
$this->checkNamespace($child->getName());
$s .= '+ '.$child->getName();
diff --git a/src/PHPCR/Util/Console/Command/BaseCommand.php b/src/PHPCR/Util/Console/Command/BaseCommand.php
index eb6d82b4..5fdd56e7 100644
--- a/src/PHPCR/Util/Console/Command/BaseCommand.php
+++ b/src/PHPCR/Util/Console/Command/BaseCommand.php
@@ -6,6 +6,8 @@
use PHPCR\Util\Console\Helper\PhpcrConsoleDumperHelper;
use PHPCR\Util\Console\Helper\PhpcrHelper;
use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Helper\DialogHelper;
+use Symfony\Component\Console\Helper\QuestionHelper;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ConfirmationQuestion;
@@ -51,20 +53,20 @@ protected function getPhpcrConsoleDumperHelper()
*
* @param InputInterface $input
* @param OutputInterface $output
- * @param string $question
+ * @param string $questionText
* @param string $default
*
* @return string
*/
- protected function ask(InputInterface $input, OutputInterface $output, $question, $default = null)
+ protected function ask(InputInterface $input, OutputInterface $output, $questionText, $default = null)
{
if ($this->getHelperSet()->has('question')) {
- $question = new Question($question, $default);
+ $question = new Question($questionText, $default);
- return $this->getHelper('question')->ask($input, $output, $question);
+ return $this->getQuestionHelper()->ask($input, $output, $question);
}
- return $this->getHelper('dialog')->ask($output, $question, $default);
+ return $this->getDialogHelper()->ask($output, $questionText, $default);
}
/**
@@ -72,19 +74,29 @@ protected function ask(InputInterface $input, OutputInterface $output, $question
*
* @param InputInterface $input
* @param OutputInterface $output
- * @param string $question
+ * @param string $questionText
* @param bool $default
*
* @return string
*/
- protected function askConfirmation(InputInterface $input, OutputInterface $output, $question, $default = true)
+ protected function askConfirmation(InputInterface $input, OutputInterface $output, $questionText, $default = true)
{
if ($this->getHelperSet()->has('question')) {
- $question = new ConfirmationQuestion($question, $default);
+ $question = new ConfirmationQuestion($questionText, $default);
- return $this->getHelper('question')->ask($input, $output, $question);
+ return $this->getQuestionHelper()->ask($input, $output, $question);
}
- return $this->getHelper('dialog')->askConfirmation($output, $question, $default);
+ return $this->getDialogHelper()->askConfirmation($output, $questionText, $default);
+ }
+
+ private function getQuestionHelper(): QuestionHelper
+ {
+ return $this->getHelper('question');
+ }
+
+ private function getDialogHelper(): DialogHelper
+ {
+ return $this->getHelper('dialog');
}
}
diff --git a/src/PHPCR/Util/Console/Command/NodeRemoveCommand.php b/src/PHPCR/Util/Console/Command/NodeRemoveCommand.php
index f9a31275..280fb5a7 100644
--- a/src/PHPCR/Util/Console/Command/NodeRemoveCommand.php
+++ b/src/PHPCR/Util/Console/Command/NodeRemoveCommand.php
@@ -107,7 +107,7 @@ protected function execute(InputInterface $input, OutputInterface $output)
if ($onlyChildren) {
$baseNode = $session->getNode($path, 0);
- /** @var $childNode NodeInterface */
+ /** @var NodeInterface $childNode */
foreach ($baseNode->getNodes() as $childNode) {
$childNodePath = $childNode->getPath();
$childNode->remove();
diff --git a/src/PHPCR/Util/Console/Command/NodesUpdateCommand.php b/src/PHPCR/Util/Console/Command/NodesUpdateCommand.php
index 6b13eb22..50aa22d0 100644
--- a/src/PHPCR/Util/Console/Command/NodesUpdateCommand.php
+++ b/src/PHPCR/Util/Console/Command/NodesUpdateCommand.php
@@ -125,7 +125,7 @@ protected function execute(InputInterface $input, OutputInterface $output)
$persistIn = $persistCounter;
- /** @var $row RowInterface */
+ /** @var RowInterface $row */
foreach ($result as $i => $row) {
$output->writeln(sprintf(
'Updating node: [%d] %s.',
@@ -169,7 +169,7 @@ private function shouldExecute(InputInterface $input, OutputInterface $output, Q
)));
if ($response === 'L') {
- /** @var $row RowInterface */
+ /** @var RowInterface $row */
foreach ($result as $i => $row) {
$output->writeln(sprintf(' - [%d] %s', $i, $row->getPath()));
}
diff --git a/src/PHPCR/Util/Console/Helper/PhpcrHelper.php b/src/PHPCR/Util/Console/Helper/PhpcrHelper.php
index 8c0d7ce2..edb6edc4 100644
--- a/src/PHPCR/Util/Console/Helper/PhpcrHelper.php
+++ b/src/PHPCR/Util/Console/Helper/PhpcrHelper.php
@@ -129,7 +129,7 @@ public function processNode(OutputInterface $output, NodeInterface $node, array
if ($operations['dump']) {
$output->writeln('Node dump: ');
- /** @var $property PropertyInterface */
+ /** @var PropertyInterface $property */
foreach ($node->getProperties() as $property) {
$value = $property->getValue();
if (!is_string($value)) {
diff --git a/src/PHPCR/Util/Console/Helper/TreeDumper/ConsoleDumperNodeVisitor.php b/src/PHPCR/Util/Console/Helper/TreeDumper/ConsoleDumperNodeVisitor.php
index f6f710dc..c64438b3 100644
--- a/src/PHPCR/Util/Console/Helper/TreeDumper/ConsoleDumperNodeVisitor.php
+++ b/src/PHPCR/Util/Console/Helper/TreeDumper/ConsoleDumperNodeVisitor.php
@@ -58,7 +58,7 @@ public function setShowFullPath($showFullPath)
public function visit(ItemInterface $item)
{
if (!$item instanceof NodeInterface) {
- throw new Exception("Internal error: did not expect to visit a non-node object: $item");
+ throw new Exception('Internal error: did not expect to visit a non-node object: '.get_class($item));
}
if ($item->getDepth() === 0) {
diff --git a/src/PHPCR/Util/Console/Helper/TreeDumper/ConsoleDumperPropertyVisitor.php b/src/PHPCR/Util/Console/Helper/TreeDumper/ConsoleDumperPropertyVisitor.php
index 3cc44664..63ccbc7b 100644
--- a/src/PHPCR/Util/Console/Helper/TreeDumper/ConsoleDumperPropertyVisitor.php
+++ b/src/PHPCR/Util/Console/Helper/TreeDumper/ConsoleDumperPropertyVisitor.php
@@ -27,6 +27,11 @@ class ConsoleDumperPropertyVisitor extends ConsoleDumperItemVisitor
*/
protected $expandReferences;
+ /**
+ * @var string
+ */
+ private $refFormat;
+
/**
* Instantiate property visitor.
*
diff --git a/src/PHPCR/Util/NodeHelper.php b/src/PHPCR/Util/NodeHelper.php
index 7997c25c..fada226e 100644
--- a/src/PHPCR/Util/NodeHelper.php
+++ b/src/PHPCR/Util/NodeHelper.php
@@ -86,14 +86,14 @@ public static function purgeWorkspace(SessionInterface $session)
{
$root = $session->getRootNode();
- /** @var $property PropertyInterface */
+ /** @var PropertyInterface $property */
foreach ($root->getProperties() as $property) {
if (!self::isSystemItem($property)) {
$property->remove();
}
}
- /** @var $node NodeInterface */
+ /** @var NodeInterface $node */
foreach ($root->getNodes() as $node) {
if (!self::isSystemItem($node)) {
$node->remove();
diff --git a/src/PHPCR/Util/QOM/BaseQomToSqlQueryConverter.php b/src/PHPCR/Util/QOM/BaseQomToSqlQueryConverter.php
index c2a1ac21..9cab6b0c 100644
--- a/src/PHPCR/Util/QOM/BaseQomToSqlQueryConverter.php
+++ b/src/PHPCR/Util/QOM/BaseQomToSqlQueryConverter.php
@@ -248,7 +248,6 @@ protected function convertPropertyValue(QOM\PropertyValueInterface $value)
protected function convertOrderings(array $orderings)
{
$list = [];
- /** @var $ordering QOM\OrderingInterface */
foreach ($orderings as $ordering) {
$order = $this->generator->evalOrder($ordering->getOrder());
$operand = $this->convertDynamicOperand($ordering->getOperand());
@@ -314,7 +313,6 @@ protected function convertColumns(array $columns)
{
$list = [];
- /** @var $column QOM\ColumnInterface */
foreach ($columns as $column) {
$selector = $column->getSelectorName();
$property = $column->getPropertyName();
diff --git a/src/PHPCR/Util/QOM/BaseSqlGenerator.php b/src/PHPCR/Util/QOM/BaseSqlGenerator.php
index 027318e7..e023fbdc 100644
--- a/src/PHPCR/Util/QOM/BaseSqlGenerator.php
+++ b/src/PHPCR/Util/QOM/BaseSqlGenerator.php
@@ -40,17 +40,17 @@ public function __construct(ValueConverter $valueConverter)
*/
public function evalQuery($source, $columns, $constraint = '', $orderings = '')
{
- $sql1 = "SELECT $columns FROM $source";
+ $sql = "SELECT $columns FROM $source";
if ($constraint) {
- $sql1 .= " WHERE $constraint";
+ $sql .= " WHERE $constraint";
}
if ($orderings) {
- $sql1 .= " ORDER BY $orderings";
+ $sql .= " ORDER BY $orderings";
}
- return $sql1;
+ return $sql;
}
/**
@@ -169,17 +169,17 @@ public function evalUpper($operand)
*/
public function evalOrderings($orderings)
{
- $sql2 = '';
+ $sql = '';
foreach ($orderings as $ordering) {
- if ($sql2 !== '') {
- $sql2 .= ', ';
+ if ($sql !== '') {
+ $sql .= ', ';
}
- $sql2 .= $ordering;
+ $sql .= $ordering;
}
- return $sql2;
+ return $sql;
}
/**
@@ -292,12 +292,83 @@ public function evalLiteral($literal)
*/
abstract public function evalCastLiteral($literal, $type);
+ /**
+ * @param string $nodeTypeName The node type of the selector. If it does not contain starting and ending
+ * brackets ([]), they will be added automatically.
+ * @param string|null $selectorName The selector name. If it is different than the nodeTypeName, the alias is
+ * declared if supported by the SQL dialect.
+ *
+ * @return string
+ */
+ abstract public function evalSelector($nodeTypeName, $selectorName = null);
+
/**
* Evaluate a path. This is different between SQL1 and SQL2.
*
* @param string $path
*
- * @return string
+ * @return string|null
*/
abstract public function evalPath($path);
+
+ /**
+ * columns ::= (Column ',' {Column}) | '*'.
+ *
+ * With empty columns, SQL1 is different from SQL2
+ *
+ * @param $columns
+ *
+ * @return string
+ */
+ abstract public function evalColumns($columns);
+
+ /**
+ * @param string $selectorName
+ * @param string $propertyName
+ * @param string $colname
+ *
+ * @return string
+ */
+ abstract public function evalColumn($selectorName, $propertyName = null, $colname = null);
+
+ /**
+ * @param $selectorName
+ * @param $propertyName
+ *
+ * @return string
+ */
+ abstract public function evalPropertyExistence($selectorName, $propertyName);
+
+ /**
+ * @param string $propertyName
+ * @param string $selectorName
+ *
+ * @return string
+ */
+ abstract public function evalPropertyValue($propertyName, $selectorName = null);
+
+ /**
+ * @param string $path
+ * @param string $selectorName
+ *
+ * @return string
+ */
+ abstract public function evalChildNode($path, $selectorName = null);
+
+ /**
+ * @param string $path
+ * @param string $selectorName
+ *
+ * @return string
+ */
+ abstract public function evalDescendantNode($path, $selectorName = null);
+
+ /**
+ * @param string $selectorName
+ * @param string $searchExpression
+ * @param string $propertyName
+ *
+ * @return string
+ */
+ abstract public function evalFullTextSearch($selectorName, $searchExpression, $propertyName = null);
}
diff --git a/src/PHPCR/Util/QOM/QomToSql2QueryConverter.php b/src/PHPCR/Util/QOM/QomToSql2QueryConverter.php
index 7c92ed62..fdef9c07 100644
--- a/src/PHPCR/Util/QOM/QomToSql2QueryConverter.php
+++ b/src/PHPCR/Util/QOM/QomToSql2QueryConverter.php
@@ -51,6 +51,10 @@ protected function convertSource(QOM\SourceInterface $source)
*/
protected function convertJoin(QOM\JoinInterface $join)
{
+ if (!$this->generator instanceof Sql2Generator) {
+ throw new NotSupportedOperandException('Only SQL2 supports join');
+ }
+
$left = $this->convertSource($join->getLeft());
$right = $this->convertSource($join->getRight());
$condition = $this->convertJoinCondition($join->getJoinCondition());
@@ -73,19 +77,20 @@ protected function convertJoin(QOM\JoinInterface $join)
protected function convertJoinCondition(QOM\JoinConditionInterface $condition)
{
if ($condition instanceof QOM\EquiJoinConditionInterface) {
- $sql2 = $this->convertEquiJoinCondition($condition);
- } elseif ($condition instanceof QOM\SameNodeJoinConditionInterface) {
- $sql2 = $this->convertSameNodeJoinCondition($condition);
- } elseif ($condition instanceof QOM\ChildNodeJoinConditionInterface) {
- $sql2 = $this->convertChildNodeJoinCondition($condition);
- } elseif ($condition instanceof QOM\DescendantNodeJoinConditionInterface) {
- $sql2 = $this->convertDescendantNodeJoinCondition($condition);
- } else {
- // This should not happen, but who knows...
- throw new InvalidArgumentException('Invalid operand');
+ return $this->convertEquiJoinCondition($condition);
+ }
+ if ($condition instanceof QOM\SameNodeJoinConditionInterface) {
+ return $this->convertSameNodeJoinCondition($condition);
+ }
+ if ($condition instanceof QOM\ChildNodeJoinConditionInterface) {
+ return $this->convertChildNodeJoinCondition($condition);
+ }
+ if ($condition instanceof QOM\DescendantNodeJoinConditionInterface) {
+ return $this->convertDescendantNodeJoinCondition($condition);
}
- return $sql2;
+ // This should not happen, but who knows...
+ throw new InvalidArgumentException('Invalid operand');
}
/**
@@ -102,6 +107,10 @@ protected function convertJoinCondition(QOM\JoinConditionInterface $condition)
*/
protected function convertEquiJoinCondition(QOM\EquiJoinConditionInterface $condition)
{
+ if (!$this->generator instanceof Sql2Generator) {
+ throw new NotSupportedOperandException('Only SQL2 supports equi join condition');
+ }
+
return $this->generator->evalEquiJoinCondition(
$condition->getSelector1Name(),
$condition->getProperty1Name(),
@@ -123,6 +132,10 @@ protected function convertEquiJoinCondition(QOM\EquiJoinConditionInterface $cond
*/
protected function convertSameNodeJoinCondition(QOM\SameNodeJoinConditionInterface $condition)
{
+ if (!$this->generator instanceof Sql2Generator) {
+ throw new NotSupportedOperandException('Only SQL2 supports same node join condition');
+ }
+
return $this->generator->evalSameNodeJoinCondition(
$condition->getSelector1Name(),
$condition->getSelector2Name(),
@@ -143,6 +156,10 @@ protected function convertSameNodeJoinCondition(QOM\SameNodeJoinConditionInterfa
*/
protected function convertChildNodeJoinCondition(QOM\ChildNodeJoinConditionInterface $condition)
{
+ if (!$this->generator instanceof Sql2Generator) {
+ throw new NotSupportedOperandException('Only SQL2 supports child node join condition');
+ }
+
return $this->generator->evalChildNodeJoinCondition(
$condition->getChildSelectorName(),
$condition->getParentSelectorName()
@@ -162,6 +179,10 @@ protected function convertChildNodeJoinCondition(QOM\ChildNodeJoinConditionInter
*/
protected function convertDescendantNodeJoinCondition(QOM\DescendantNodeJoinConditionInterface $condition)
{
+ if (!$this->generator instanceof Sql2Generator) {
+ throw new NotSupportedOperandException('Only SQL2 supports descendant node join condition');
+ }
+
return $this->generator->evalDescendantNodeJoinCondition(
$condition->getDescendantSelectorName(),
$condition->getAncestorSelectorName()
@@ -221,11 +242,16 @@ protected function convertConstraint(QOM\ConstraintInterface $constraint)
if ($constraint instanceof QOM\PropertyExistenceInterface) {
return $this->convertPropertyExistence($constraint);
- } elseif ($constraint instanceof QOM\FullTextSearchInterface) {
+ }
+ if ($constraint instanceof QOM\FullTextSearchInterface) {
return $this->convertFullTextSearch($constraint);
}
if ($constraint instanceof QOM\SameNodeInterface) {
+ if (!$this->generator instanceof Sql2Generator) {
+ throw new NotSupportedConstraintException('Only SQL2 supports same node constraint');
+ }
+
return $this->generator->evalSameNode(
$this->convertPath($constraint->getPath()),
$constraint->getSelectorName()
@@ -275,31 +301,47 @@ protected function convertDynamicOperand(QOM\DynamicOperandInterface $operand)
}
if ($operand instanceof QOM\LengthInterface) {
+ if (!$this->generator instanceof Sql2Generator) {
+ throw new NotSupportedOperandException('Only SQL2 supports length operand');
+ }
+
return $this->generator->evalLength($this->convertPropertyValue($operand->getPropertyValue()));
}
if ($operand instanceof QOM\NodeNameInterface) {
+ if (!$this->generator instanceof Sql2Generator) {
+ throw new NotSupportedOperandException('Only SQL2 supports node operand');
+ }
+
return $this->generator->evalNodeName($operand->getSelectorName());
}
if ($operand instanceof QOM\NodeLocalNameInterface) {
+ if (!$this->generator instanceof Sql2Generator) {
+ throw new NotSupportedOperandException('Only SQL2 supports local node name operand');
+ }
+
return $this->generator->evalNodeLocalName($operand->getSelectorName());
}
if ($operand instanceof QOM\FullTextSearchScoreInterface) {
+ if (!$this->generator instanceof Sql2Generator) {
+ throw new NotSupportedOperandException('Only SQL2 supports fulltext search score operand');
+ }
+
return $this->generator->evalFullTextSearchScore($operand->getSelectorName());
}
if ($operand instanceof QOM\LowerCaseInterface) {
- $operand = $this->convertDynamicOperand($operand->getOperand());
+ $operandName = $this->convertDynamicOperand($operand->getOperand());
- return $this->generator->evalLower($operand);
+ return $this->generator->evalLower($operandName);
}
if ($operand instanceof QOM\UpperCaseInterface) {
- $operand = $this->convertDynamicOperand($operand->getOperand());
+ $operandName = $this->convertDynamicOperand($operand->getOperand());
- return $this->generator->evalUpper($operand);
+ return $this->generator->evalUpper($operandName);
}
// This should not happen, but who knows...
diff --git a/src/PHPCR/Util/QOM/Sql1Generator.php b/src/PHPCR/Util/QOM/Sql1Generator.php
index c06a0612..6c9927a2 100644
--- a/src/PHPCR/Util/QOM/Sql1Generator.php
+++ b/src/PHPCR/Util/QOM/Sql1Generator.php
@@ -64,11 +64,12 @@ public function evalChildNode($path, $selectorName = null)
/**
* Emulate descendant query with LIKE query.
*
- * @param string $path
+ * @param string $path
+ * @param string|null $selectorName Unused
*
* @return string
*/
- public function evalDescendantNode($path)
+ public function evalDescendantNode($path, $selectorName = null)
{
$path = $this->getPathForDescendantQuery($path);
@@ -161,10 +162,11 @@ public function evalPropertyValue($propertyName, $selectorName = null)
*
* @param string $selectorName unused in SQL1
* @param string $propertyName
+ * @param string $colname unused in SQL1
*
* @return string
*/
- public function evalColumn($selectorName = null, $propertyName = null)
+ public function evalColumn($selectorName = null, $propertyName = null, $colname = null)
{
return $propertyName;
}
diff --git a/src/PHPCR/Util/QOM/Sql2Generator.php b/src/PHPCR/Util/QOM/Sql2Generator.php
index a9389117..45fd7751 100644
--- a/src/PHPCR/Util/QOM/Sql2Generator.php
+++ b/src/PHPCR/Util/QOM/Sql2Generator.php
@@ -237,7 +237,7 @@ public function evalPropertyExistence($selectorName, $propertyName)
* FullTextSearchExpression ')'
* FullTextSearchExpression ::= BindVariable | ''' FullTextSearchLiteral '''.
*
- * @param string $selectorName unusued
+ * @param string $selectorName
* @param string $searchExpression
* @param string $propertyName
*
@@ -389,18 +389,19 @@ public function evalColumn($selectorName, $propertyName = null, $colname = null)
*/
public function evalPath($path)
{
- if ($path) {
- $sql2 = $path;
- // only ensure proper quoting if the user did not quote himself, we trust him to get it right if he did.
- if (substr($path, 0, 1) !== '[' && substr($path, -1) !== ']') {
- if (false !== strpos($sql2, ' ') || false !== strpos($sql2, '.')) {
- $sql2 = '"'.$sql2.'"';
- }
- $sql2 = '['.$sql2.']';
+ if (!$path) {
+ return $path;
+ }
+ $sql2 = $path;
+ // only ensure proper quoting if the user did not quote himself, we trust him to get it right if he did.
+ if (strpos($path, '[') !== 0 && substr($path, -1) !== ']') {
+ if (false !== strpos($sql2, ' ') || false !== strpos($sql2, '.')) {
+ $sql2 = '"'.$sql2.'"';
}
-
- return $sql2;
+ $sql2 = '['.$sql2.']';
}
+
+ return $sql2;
}
/**
diff --git a/src/PHPCR/Util/QOM/Sql2ToQomQueryConverter.php b/src/PHPCR/Util/QOM/Sql2ToQomQueryConverter.php
index 40a336bd..76aca66b 100644
--- a/src/PHPCR/Util/QOM/Sql2ToQomQueryConverter.php
+++ b/src/PHPCR/Util/QOM/Sql2ToQomQueryConverter.php
@@ -69,6 +69,11 @@ class Sql2ToQomQueryConverter
*/
protected $implicitSelectorName = null;
+ /**
+ * @var ValueConverter
+ */
+ private $valueConverter;
+
/**
* Instantiate a converter.
*
diff --git a/src/PHPCR/Util/TraversingItemVisitor.php b/src/PHPCR/Util/TraversingItemVisitor.php
index 447b35dc..713df4c8 100644
--- a/src/PHPCR/Util/TraversingItemVisitor.php
+++ b/src/PHPCR/Util/TraversingItemVisitor.php
@@ -159,7 +159,15 @@ public function visit(ItemInterface $item)
$this->entering($item, $this->currentDepth);
$this->leaving($item, $this->currentDepth);
} else {
- /* @var $item NodeInterface */
+ if (!$item instanceof NodeInterface) {
+ throw new RepositoryException(sprintf(
+ 'Internal error in TraversingItemVisitor: item %s at %s is not a node but %s',
+ $item->getName(),
+ $item->getPath(),
+ get_class($item)
+ ));
+ }
+
try {
if ($this->breadthFirst === false) {
$this->entering($item, $this->currentDepth);
diff --git a/src/PHPCR/Util/TreeWalker.php b/src/PHPCR/Util/TreeWalker.php
index 670c4133..94b460f1 100644
--- a/src/PHPCR/Util/TreeWalker.php
+++ b/src/PHPCR/Util/TreeWalker.php
@@ -122,20 +122,26 @@ protected function mustVisitProperty(PropertyInterface $property)
* @param int $recurse Max recursion level
* @param int $level Recursion level
*/
- public function traverse(NodeInterface $node, $recurse = -1, $level = 0)
+ public function traverse(NodeInterface $node, $recurse = -1, $level = 0): void
{
if ($this->mustVisitNode($node)) {
// Visit node
- $this->nodeVisitor->setLevel($level);
- $this->nodeVisitor->setShowFullPath(0 === $level);
+ if (method_exists($this->nodeVisitor, 'setLevel')) {
+ $this->nodeVisitor->setLevel($level);
+ }
+ if (method_exists($this->nodeVisitor, 'setShowFullPath')) {
+ $this->nodeVisitor->setShowFullPath(0 === $level);
+ }
$node->accept($this->nodeVisitor);
// Visit properties
if ($this->propertyVisitor !== null) {
foreach ($node->getProperties() as $prop) {
if ($this->mustVisitProperty($prop)) {
- $this->propertyVisitor->setLevel($level);
+ if (method_exists($this->propertyVisitor, 'setLevel')) {
+ $this->propertyVisitor->setLevel($level);
+ }
$prop->accept($this->propertyVisitor);
}
}
diff --git a/tests/PHPCR/Tests/Util/CND/Reader/FileReaderTest.php b/tests/PHPCR/Tests/Util/CND/Reader/FileReaderTest.php
index 23329e48..5d3d1a2d 100644
--- a/tests/PHPCR/Tests/Util/CND/Reader/FileReaderTest.php
+++ b/tests/PHPCR/Tests/Util/CND/Reader/FileReaderTest.php
@@ -23,6 +23,11 @@ class FileReaderTest extends TestCase
*/
private $lines;
+ /**
+ * @var string[]
+ */
+ private $chars;
+
public function setUp(): void
{
$this->filepath = __DIR__.'/../Fixtures/files/TestFile.txt';
diff --git a/tests/PHPCR/Tests/Util/CND/Scanner/GenericScannerTest.php b/tests/PHPCR/Tests/Util/CND/Scanner/GenericScannerTest.php
index dab5d01e..59f9377d 100644
--- a/tests/PHPCR/Tests/Util/CND/Scanner/GenericScannerTest.php
+++ b/tests/PHPCR/Tests/Util/CND/Scanner/GenericScannerTest.php
@@ -10,8 +10,6 @@
use PHPCR\Util\CND\Scanner\TokenFilter;
use PHPCR\Util\CND\Scanner\TokenQueue;
use PHPUnit\Framework\TestCase;
-use Test;
-use TestClass;
class GenericScannerTest extends TestCase
{
@@ -27,7 +25,7 @@ class GenericScannerTest extends TestCase
// namespace Test\Foobar;
[Token::TK_IDENTIFIER, 'namespace'],
[Token::TK_WHITESPACE, ''],
- [Token::TK_IDENTIFIER, Test::class],
+ [Token::TK_IDENTIFIER, 'Test'],
[Token::TK_SYMBOL, '\\'],
[Token::TK_IDENTIFIER, 'Foobar'],
[Token::TK_SYMBOL, ';'],
@@ -37,7 +35,7 @@ class GenericScannerTest extends TestCase
// class TestClass {
[Token::TK_IDENTIFIER, 'class'],
[Token::TK_WHITESPACE, ''],
- [Token::TK_IDENTIFIER, TestClass::class],
+ [Token::TK_IDENTIFIER, 'TestClass'],
[Token::TK_NEWLINE, ''],
[Token::TK_SYMBOL, '{'],
[Token::TK_NEWLINE, ''],
diff --git a/tests/PHPCR/Tests/Util/QOM/BaseSqlGeneratorTest.php b/tests/PHPCR/Tests/Util/QOM/BaseSqlGeneratorTest.php
index e5ed115b..0f11e57d 100644
--- a/tests/PHPCR/Tests/Util/QOM/BaseSqlGeneratorTest.php
+++ b/tests/PHPCR/Tests/Util/QOM/BaseSqlGeneratorTest.php
@@ -2,10 +2,16 @@
namespace PHPCR\Tests\Util\QOM;
+use PHPCR\Util\QOM\BaseSqlGenerator;
use PHPUnit\Framework\TestCase;
abstract class BaseSqlGeneratorTest extends TestCase
{
+ /**
+ * @var BaseSqlGenerator
+ */
+ protected $generator;
+
public function testNot()
{
$string = $this->generator->evalNot('foo = bar');