From 68a5253087391d47ae1c6efa37df0b099025f1e7 Mon Sep 17 00:00:00 2001 From: Bene Date: Thu, 1 Oct 2015 10:21:56 +0200 Subject: [PATCH] Fix formatting, do some refactoring and add tests --- .gitignore | 1 + README.md | 4 ++ src/QueryBuilder.php | 119 +++++++++++++++++++------------- tests/unit/QueryBuilderTest.php | 30 ++++++++ 4 files changed, 107 insertions(+), 47 deletions(-) diff --git a/.gitignore b/.gitignore index 1aa95b0..9cc33ac 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ composer.phar vendor/ composer.lock +.idea/ diff --git a/README.md b/README.md index 74716d1..b9fd289 100644 --- a/README.md +++ b/README.md @@ -183,6 +183,10 @@ via TravisCI, as a TravisCI configuration file is also provided in the root dire ## Release notes +### 0.3.2 (2015-10-01) + +* Added `QueryBuilder::describe` + ### 0.3.1 (2015-09-06) * Added support for native values in selects diff --git a/src/QueryBuilder.php b/src/QueryBuilder.php index 4f01e27..6d0ed73 100644 --- a/src/QueryBuilder.php +++ b/src/QueryBuilder.php @@ -4,6 +4,7 @@ use InvalidArgumentException; use RangeException; +use RuntimeException; /** * Abstraction layer to build SPARQL queries @@ -18,23 +19,6 @@ */ class QueryBuilder { - /** - * Select query - */ - CONST TYPE_SELECT = 'SELECT'; - - /** - * Describe query - */ - CONST TYPE_DESCRIBE = 'DESCRIBE'; - - /** - * Current type defaults to select) - * - * @var string - */ - private $currentType = self::TYPE_SELECT; - /** * @var ExpressionValidator */ @@ -60,6 +44,11 @@ class QueryBuilder { */ private $uniqueness = ''; + /** + * @var string form of the query, one of SELECT or DESCRIBE + */ + private $queryForm = null; + /** * @var GraphBuilder */ @@ -90,18 +79,6 @@ public function __construct( array $prefixes = array() ) { public function getSelects() { return $this->selects; } - - /** - * Specifies the expressions to describe. - * - * @param string|string[] $expressions - * @return self - * @throws InvalidArgumentException - */ - public function describe($expressions) { - $this->currentType = self::TYPE_DESCRIBE; - return $this->select($expressions); - } /** * Specifies the expressions to select. @@ -109,29 +86,18 @@ public function describe($expressions) { * @param string|string[] $expressions * @return self * @throws InvalidArgumentException + * @throws RuntimeException */ public function select( $expressions /* expressions ... */ ) { $expressions = is_array( $expressions ) ? $expressions : func_get_args(); - foreach ( $expressions as $expression ) { - $this->expressionValidator->validate( $expression, - ExpressionValidator::VALIDATE_VARIABLE | ExpressionValidator::VALIDATE_FUNCTION_AS - ); - - // @todo temp hack to add AS definitions to defined variables - $regexHelper = new RegexHelper(); - $matches = $regexHelper->getMatches( 'AS \{variable}', $expression ); - $this->usageValidator->trackDefinedVariables( $matches ); - - // @todo detect functions and wrap with brackets automatically - $this->usageValidator->trackUsedVariables( $expression ); - $this->selects[] = $expression; - } + $this->setQueryForm( 'SELECT' ); + $this->addExpressions( $expressions ); return $this; } - /** + /** * Specifies the expressions to select. Duplicate results are eliminated. * * @since 0.3 @@ -141,8 +107,12 @@ public function select( $expressions /* expressions ... */ ) { * @throws InvalidArgumentException */ public function selectDistinct( $expressions /* expressions ... */ ) { - call_user_func_array( array( $this, 'select' ), func_get_args() ); + $expressions = is_array( $expressions ) ? $expressions : func_get_args(); + + $this->setQueryForm( 'SELECT' ); $this->uniqueness = 'DISTINCT '; + $this->addExpressions( $expressions ); + return $this; } @@ -156,11 +126,65 @@ public function selectDistinct( $expressions /* expressions ... */ ) { * @throws InvalidArgumentException */ public function selectReduced( $expressions /* expressions ... */ ) { - call_user_func_array( array( $this, 'select' ), func_get_args() ); + $expressions = is_array( $expressions ) ? $expressions : func_get_args(); + + $this->setQueryForm( 'SELECT' ); $this->uniqueness = 'REDUCED '; + $this->addExpressions( $expressions ); + return $this; } + /** + * Specifies the expressions to describe. + * + * @since 0.4 + * + * @param string|string[] $expressions + * @return self + * @throws InvalidArgumentException + * @throws RuntimeException + */ + public function describe( $expressions /* expressions ... */ ) { + $expressions = is_array( $expressions ) ? $expressions : func_get_args(); + + $this->setQueryForm( 'DESCRIBE' ); + $this->addExpressions( + $expressions, + ExpressionValidator::VALIDATE_VARIABLE | ExpressionValidator::VALIDATE_FUNCTION_AS + | ExpressionValidator::VALIDATE_IRI | ExpressionValidator::VALIDATE_PREFIXED_IRI + ); + + return $this; + } + + private function setQueryForm( $queryForm ) { + if ( $this->queryForm !== null ) { + throw new RuntimeException( 'Query type is already set to ' . $this->queryForm ); + } + + $this->queryForm = $queryForm; + } + + private function addExpressions( array $expressions, $options = null ) { + foreach ( $expressions as $expression ) { + $this->expressionValidator->validate( + $expression, + $options ?: ExpressionValidator::VALIDATE_VARIABLE | ExpressionValidator::VALIDATE_FUNCTION_AS + ); + + // @todo temp hack to add AS definitions to defined variables + $regexHelper = new RegexHelper(); + $matches = $regexHelper->getMatches( 'AS \{variable}', $expression ); + $this->usageValidator->trackDefinedVariables( $matches ); + + // @todo detect functions and wrap with brackets automatically + $this->usageValidator->trackUsedVariables( $expression ); + $this->usageValidator->trackUsedPrefixes( $expression ); + $this->selects[] = $expression; + } + } + /** * Adds the given triple as a condition. * @@ -377,7 +401,8 @@ public function getSPARQL( $includePrefixes = true ) { $this->usageValidator->validate(); $sparql = $includePrefixes ? $this->prefixBuilder->getSPARQL() : ''; - $sparql .= $this->currentType . ' ' . $this->uniqueness . $this->formatSelects() . ' WHERE'; + $sparql .= $this->queryForm ?: 'SELECT'; + $sparql .= ' ' . $this->uniqueness . $this->formatSelects() . ' WHERE'; $sparql .= ' {' . $this->graphBuilder->getSPARQL() . ' }'; $sparql .= $this->modifierBuilder->getSPARQL(); diff --git a/tests/unit/QueryBuilderTest.php b/tests/unit/QueryBuilderTest.php index 3c6dc65..0debf0b 100644 --- a/tests/unit/QueryBuilderTest.php +++ b/tests/unit/QueryBuilderTest.php @@ -67,6 +67,36 @@ public function testSelectReduced() { ); } + public function testDescribe() { + $queryBuilder = new QueryBuilder(); + $this->assertSame( + $queryBuilder, + $queryBuilder->describe( '?a', '?b', '' ) + ); + + // use variables ?a and ?b + $queryBuilder->where( '?a', '?b', '?c' ); + + $this->assertEquals( + 'DESCRIBE ?a ?b WHERE { ?a ?b ?c . }', + $queryBuilder->getSPARQL() + ); + } + + public function testDescribe_undefinedPrefix() { + $queryBuilder = new QueryBuilder(); + $this->assertSame( + $queryBuilder, + $queryBuilder->describe( 'foo:bar' ) + ); + + // use variables ?a and ?b + $queryBuilder->where( '?a', '?b', '?c' ); + + $this->setExpectedException( 'RangeException' ); + $queryBuilder->getSPARQL(); + } + public function testWhere() { $queryBuilder = new QueryBuilder(); $this->assertSame(