Skip to content

Commit d264bf0

Browse files
committed
Merge branch 'develop'
# Conflicts: # CHANGELOG.md
2 parents ea135f2 + 4623101 commit d264bf0

10 files changed

+434
-25
lines changed

CHANGELOG.md

+9-7
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,22 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to
66
[Semantic Versioning](http://semver.org/).
77

8+
## [1.2.0] - 2017-12-20
9+
* Add management of date and time parsing and conversion into compliant database string format.
10+
811
## [1.1.3] - 2017-12-01
912
* Minor fixes of case of empty string value.
1013

1114
## [1.1.2] - 2017-11-30
12-
* Minor fixes of README.md.
15+
* Minor fixes of README.md.
1316

1417
## [1.1.1] - 2017-11-28
15-
* Minor fixes for PHP 5.6.
18+
* Minor fixes for PHP 5.6.
1619

1720
## [1.1.0] - 2017-11-28
18-
* Add support for expressions with simple logical operators and without parenthesis. The or operator is writen with `-`,
19-
the and operator is written with `+` ;
20-
* Update minor composer dependency versions.
21+
* Add support for expressions with simple logical operators and without parenthesis. The or operator is writen with `-`
22+
, the and operator is written with `+` ;
23+
* Update minor composer dependency versions.
2124

2225
## [1.0.0] - 2017-08-01
23-
24-
* Initial release.
26+
* Initial release.

README.md

+34
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,40 @@ The following values can be used.
123123
* string (must be quoted with simple quotes ')
124124
* string with an ISO 8601 format for the dates
125125

126+
### Date and time parsing
127+
128+
By default when the `SqlFilterConverter` encounters a string inside an expression it simply takes it as a "standard"
129+
string.
130+
131+
But you've probably business entities having date attributes and want to request those entities using data and time
132+
filters. To do this you can set a date and time parser on the `SqlFilterConverter` to indicate him to parse date and
133+
time string and transform them to date and time string which are compliant with the database in use.
134+
135+
For example configuring the `SqlFilterConverter` to parse ISO 8601 strings and convert them to MySQL date and time format
136+
is done with the following.
137+
138+
```php
139+
$sqFilterConverter = new SqlFilterConverter();
140+
$sqlFilterConverter->setDateTimeParser(new FormatDateTimeParser());
141+
```
142+
143+
By default the `FormatDateTimeParser` class uses ISO 8601 date and time parsing, but you can change its behavior with
144+
the `FormatDateTimeParser->setFormat(string $format)` method. In generall you'll want to use one of the format provided
145+
with the PHP `DateTime` class, that's to say one of `DateTime::ATOM`, `DateTime::COOKIE`, `DateTime::ISO8601`,
146+
`DateTime::RFC822`, `DateTime::RFC850`, `DateTime::RFC1036`, `DateTime::RFC1123`, `DateTime::RFC2822`,
147+
`DateTime::RFC3339`, `DateTime::RSS` or `DateTime::W3C`.
148+
149+
The parser parses date and time strings and convert them to PHP `DateTime` object, then internally the
150+
`SqlFilterConverter` converts the `DateTime` object to a string which is compatible with Mysql.
151+
152+
153+
For example the following transform will create a `property <= ?` expression with a value equals to
154+
`2017-12-01 06:00:00` which is compatible with MySQL.
155+
156+
```php
157+
$sqlFilter = $filterConverter->transform('property', "<='2017-12-01T06:00:00Z'");
158+
```
159+
126160
## About Gomoob
127161

128162
At [Gomoob](https://www.gomoob.com) we build high quality software with awesome Open Source frameworks everyday. Would
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
3+
/**
4+
* BSD 3-Clause License
5+
*
6+
* Copyright (c) 2017, GOMOOB All rights reserved.
7+
*
8+
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
9+
* following conditions are met:
10+
*
11+
* * Redistributions of source code must retain the above copyright notice, this list of conditions and the following
12+
* disclaimer.
13+
*
14+
* * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
15+
* disclaimer in the documentation and/or other materials provided with the distribution.
16+
*
17+
* * Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote
18+
* products derived from this software without specific prior written permission.
19+
*
20+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
21+
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22+
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23+
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24+
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25+
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27+
*/
28+
namespace Gomoob\Filter;
29+
30+
/**
31+
* Interface which represents a `\DateTime` parser used to convert string to `\DateTime` objects.
32+
*
33+
* @author Baptiste GAILLARD ([email protected])
34+
*/
35+
interface DateTimeParserInterface
36+
{
37+
/**
38+
* Parse a string and converts it into a `\DateTime` object.
39+
*
40+
* @return \DateTime the resulting `\DateTime` object.
41+
*
42+
* @throws \InvalidArgumentException if the provided string has not the format expected by the parser.
43+
*/
44+
public function parse(/* string */ $str) /* : \DateTime */;
45+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
<?php
2+
3+
/**
4+
* BSD 3-Clause License
5+
*
6+
* Copyright (c) 2017, GOMOOB All rights reserved.
7+
*
8+
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
9+
* following conditions are met:
10+
*
11+
* * Redistributions of source code must retain the above copyright notice, this list of conditions and the following
12+
* disclaimer.
13+
*
14+
* * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
15+
* disclaimer in the documentation and/or other materials provided with the distribution.
16+
*
17+
* * Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote
18+
* products derived from this software without specific prior written permission.
19+
*
20+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
21+
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22+
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23+
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24+
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25+
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27+
*/
28+
namespace Gomoob\Filter\Parser;
29+
30+
use Gomoob\Filter\DateTimeParserInterface;
31+
32+
/**
33+
* Class which represents `DateTime` parser to parse string dates.
34+
*
35+
* <p>The format of the string dates can be specified using the standard <tt>\DateTime</tt> format.</p>
36+
*
37+
* <p>If you want to choose a format which is compliant with a specification you can use one of the following: </p>
38+
* <ul>
39+
* <li>DateTime::ATOM</li>
40+
* <li>DateTime::COOKIE</li>
41+
* <li>DateTime::ISO8601</li>
42+
* <li>DateTime::RFC822</li>
43+
* <li>DateTime::RFC850</li>
44+
* <li>DateTime::RFC1036</li>
45+
* <li>DateTime::RFC1123</li>
46+
* <li>DateTime::RFC2822</li>
47+
* <li>DateTime::RFC3339</li>
48+
* <li>DateTime::RSS</li>
49+
* <li>DateTime::W3C</li>
50+
* </ul>
51+
*
52+
* @author Baptiste GAILLARD ([email protected])
53+
*/
54+
class FormatDateTimeParser implements DateTimeParserInterface
55+
{
56+
/**
57+
* The format used to parse `\DateTime` strings.
58+
*
59+
* @var string
60+
*
61+
* @see http://www.php.net/manual/en/datetime.createfromformat.php
62+
*/
63+
private $format = \DateTime::ISO8601;
64+
65+
/**
66+
* {@inheritDoc}
67+
*/
68+
public function parse(/* string */ $str) /* : \DateTime */
69+
{
70+
$date = \DateTime::createFromFormat($this->format, $str);
71+
72+
// If the conversion failed
73+
if ($date === false) {
74+
// If the format is ISO8601 then we try to parse with additional formats, this is because the PHP
75+
// \DateTime:ISO8601 does not accepts all ISO8601 formats.
76+
//
77+
// see https://stackoverflow.com/questions/4411340/
78+
// php-datetimecreatefromformat-doesnt-parse-iso-8601-date-time
79+
// see https://stackoverflow.com/questions/6150280/
80+
// get-the-iso-8601-with-seconds-decimal-fraction-of-second-date-in-php
81+
// see https://bugs.php.net/bug.php?id=51950
82+
if ($this->format === \DateTime::ISO8601) {
83+
$date = \DateTime::createFromFormat('Y-m-d\TH:i:s.uP', $str);
84+
}
85+
86+
// If the conversion failed
87+
if ($date === false) {
88+
throw new \InvalidArgumentException(
89+
sprintf(
90+
'%s method received an \'%s\' date string which is not compliant with the configured ' .
91+
'format !',
92+
__METHOD__,
93+
$value
94+
)
95+
);
96+
}
97+
}
98+
99+
return $date;
100+
}
101+
102+
/**
103+
* Sets the format to us to parse date and time strings.
104+
*
105+
* <p>If you want to choose a format which is compliant with a specification you can use one of the following: </p>
106+
* <ul>
107+
* <li>DateTime::ATOM</li>
108+
* <li>DateTime::COOKIE</li>
109+
* <li>DateTime::ISO8601</li>
110+
* <li>DateTime::RFC822</li>
111+
* <li>DateTime::RFC850</li>
112+
* <li>DateTime::RFC1036</li>
113+
* <li>DateTime::RFC1123</li>
114+
* <li>DateTime::RFC2822</li>
115+
* <li>DateTime::RFC3339</li>
116+
* <li>DateTime::RSS</li>
117+
* <li>DateTime::W3C</li>
118+
* </ul>
119+
*
120+
* @param string $format the format to use to parse date and time strings.
121+
*/
122+
public function setFormat(/* string */ $format)
123+
{
124+
$this->format = $format;
125+
}
126+
}

src/main/php/Gomoob/Filter/Sql/SqlFilterConverter.php

+66-3
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,27 @@
4545
*/
4646
class SqlFilterConverter implements SqlFilterConverterInterface
4747
{
48+
/**
49+
* The parser used to parse `DateTime` strings, this parser could be `null` in which can date and time parsing is
50+
* disabled.
51+
*
52+
* @var \Gomoob\Filter\DateTimeParserInterface
53+
*/
54+
private $dateTimeParser;
55+
56+
/**
57+
* Sets the parsed used to parse `DateTime` strings.
58+
*
59+
* Set the parser to `null` to disable `DateTime` parsing. If this is defined each one a `DateTime` is encountered
60+
* it is converted to a date and time string which is compliant with the configured database.
61+
*
62+
* @param \Gomoob\Filter\DateTimeParserInterface $dateTimeParser the date and time parser to use.
63+
*/
64+
public function setDateTimeParser(/* DateTimeParserInterface */ $dateTimeParser)
65+
{
66+
$this->dateTimeParser = $dateTimeParser;
67+
}
68+
4869
/**
4970
* {@inheritDoc}
5071
*/
@@ -116,6 +137,37 @@ private function extractUnquotedString(/* TokenInterface */ $token) /* : string
116137
return substr($string, 1, strlen($string) - 2);
117138
}
118139

140+
/**
141+
* Function used to try to parse a date and time string and convert it into an equivalent string compliant with the
142+
* database currently in use.
143+
*
144+
* @param string $str the string to parse.
145+
*
146+
* @return string the resulting equivalent string date compliant with the database in use. Please not that if
147+
* parsing fails the `$str` function input parameter is returned.
148+
*/
149+
private function parseDateTime(/* string */ $str) /* : string */
150+
{
151+
$sqlDateTime = $str;
152+
153+
// If a DateTime parser is defined
154+
if ($this->dateTimeParser !== null) {
155+
// Try to parse the string using the \DateTime parser
156+
try {
157+
// Gets the PHP DateTime
158+
$dateTime = $this->dateTimeParser->parse($str);
159+
160+
// Converts the PHP DateTime to a format which is compliant with the database currently in use
161+
// For now we only support MySQL
162+
$sqlDateTime = $dateTime->format('Y-m-d H:i:s');
163+
} catch (\InvalidArgumentException $ieax) {
164+
// Expected
165+
}
166+
}
167+
168+
return $sqlDateTime;
169+
}
170+
119171
/**
120172
* Parse a filter expression from the first encountered token.
121173
*
@@ -164,9 +216,12 @@ private function parseFromFirstToken(
164216
$args[] = $this->parseNumberToken($secondToken);
165217
break;
166218
case FilterToken::STRING:
219+
// Extract string without quotes
220+
$unquotedString = $this->extractUnquotedString($secondToken);
221+
167222
// Try to find star tokens to know if the query if for a 'like'
168223
$starTokenizer = new StarTokenizer();
169-
$starTokens = $starTokenizer->tokenize($this->extractUnquotedString($secondToken));
224+
$starTokens = $starTokenizer->tokenize($unquotedString);
170225

171226
// The SQL instruction to build must contain a 'like'
172227
if (count($starTokens) > 1) {
@@ -201,7 +256,10 @@ private function parseFromFirstToken(
201256

202257
$sb .= $this->convertSimpleOperatorTokenToSqlString($firstToken);
203258
$sb .= ' ?';
204-
$args[] = $this->extractUnquotedString($secondToken);
259+
260+
// Try to take into account the string as a date, if parsing failed the unquoted string is
261+
// simply returned
262+
$args[] = $this->parseDateTime($unquotedString);
205263
}
206264

207265
break;
@@ -252,7 +310,12 @@ private function parseFromFirstToken(
252310
$sb .= $this->convertSimpleOperatorTokenToSqlString($firstToken);
253311
$sb .= ' ?';
254312

255-
$args[] = $this->extractUnquotedString($secondToken);
313+
// Extract string without quotes
314+
$unquotedString = $this->extractUnquotedString($secondToken);
315+
316+
// Try to take into account the string as a date, if parsing failed the unquoted string is
317+
// simply returned
318+
$args[] = $this->parseDateTime($unquotedString);
256319

257320
break;
258321
default:

src/main/php/Gomoob/Filter/Tokenizer/AbstractTokenizer.php

+13-4
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,15 @@
3434
*
3535
* @author Baptiste GAILLARD ([email protected])
3636
*/
37-
abstract class AbstractTokenizer implements TokenizerInterface {
37+
abstract class AbstractTokenizer implements TokenizerInterface
38+
{
39+
40+
/**
41+
* Regular expression used to identify Date and time values.
42+
*
43+
* @var string
44+
*/
45+
private $dateTimeFormat = \DateTime::ISO8601;
3846

3947
/**
4048
* List which holds all our token informations.
@@ -54,7 +62,8 @@ abstract class AbstractTokenizer implements TokenizerInterface {
5462
/**
5563
* {@inheritDoc}
5664
*/
57-
public function addTokenInfo(/* string */ $regex, /* int */ $tokenCode) {
65+
public function addTokenInfo(/* string */ $regex, /* int */ $tokenCode)
66+
{
5867
// The user can pass a regular expression string and a token code to the method. The method will then add the
5968
// "^" character to the user supplied regular expression. It causes the regular expression to match only the
6069
// beginning of a string. This is needed because we will be removing any token always looking for the next token
@@ -65,7 +74,8 @@ public function addTokenInfo(/* string */ $regex, /* int */ $tokenCode) {
6574
/**
6675
* {@inheritDoc}
6776
*/
68-
public function tokenize(/* string */ $string) /* : array */ {
77+
public function tokenize(/* string */ $string) /* : array */
78+
{
6979
$tokens = [];
7080

7181
// First we clean our string
@@ -82,7 +92,6 @@ public function tokenize(/* string */ $string) /* : array */ {
8292

8393
// If a known token has been encountered
8494
if (preg_match($info->getRegex(), $s, $matches)) {
85-
8695
// A token has been found
8796
$match = true;
8897

0 commit comments

Comments
 (0)