Skip to content

Указание модификаторов, для элементов данных плейсхолдеров values, list и set #67

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions goDB/Exceptions/SubDataInvalidFormat.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php
/**
* @package go\DB
*/

namespace go\DB\Exceptions;

final class SubDataInvalidFormat extends Data
{
/**
* @param int $placeholder
* @param int $message
* @param Logic $previous
*/
public function __construct($placeholder, $message, $previous = null)
{
$message = 'Invalid sub data for ?' . $placeholder . ': ' . $message;
parent::__construct($message, 0, $previous);
}
}
129 changes: 102 additions & 27 deletions goDB/Helpers/Templater.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
use go\DB\Exceptions\DataNotEnough;
use go\DB\Exceptions\DataNamed;
use go\DB\Exceptions\DataInvalidFormat;
use go\DB\Exceptions\Logic;
use go\DB\Exceptions\SubDataInvalidFormat;
use go\DB\Exceptions\UnknownPlaceholder;
use go\DB\Exceptions\MixedPlaceholder;

Expand Down Expand Up @@ -54,7 +56,7 @@ public function parse()
return $this->query;
}
$query = preg_replace_callback('~{(.*?)}~', array($this, 'tableClb'), $this->pattern);
$pattern = '~\?([a-z\?-]+)?(:([a-z0-9_-]*))?;?~i';
$pattern = '~\?([a-z\?-]+)?(:([a-z0-9_-]*))?(\[[?:\s\w,-]+\])?;?~i';
$callback = array($this, 'placeholderClb');
$query = preg_replace_callback($pattern, $callback, $query);
if ((!$this->named) && (count($this->data) > $this->counter)) {
Expand Down Expand Up @@ -97,17 +99,76 @@ private function tableClb($matches)
protected function placeholderClb($matches)
{
$placeholder = isset($matches[1]) ? $matches[1] : '';
if ($placeholder == '?') { // "??" for question mark
return '?';
}

$parser = $this->getParser($matches);
$dataKey = $this->currentName;
if (!array_key_exists($dataKey, $this->data)) {
if ($this->named) {
throw new DataNamed($dataKey);
} else {
throw new DataNotEnough(count($this->data), $dataKey);
}
}
$value = $this->data[$dataKey];

if (isset($matches[4])) {
$elementModifiers = $this->getElementModifiers($placeholder, $matches[4]);
} else {
$elementModifiers = [];
}
$method = 'replacement' . strtoupper($parser->getType());

return $this->$method($value, $parser->getModifiers(), $elementModifiers);
}

/**
* @param string $placeholder
* @param string $subPattern
* @return array
*/
protected function getElementModifiers($placeholder, $subPattern)
{
$subTemplate = clone $this;
$subTemplate->named = false;
$subTemplate->counter = 0;
$pattern = '~\?([a-z\?-]+)?(:([a-z0-9_-]*))?;?~i';
$elementModifiers = [];
preg_match_all($pattern, $subPattern, $subMatches, PREG_SET_ORDER);
foreach ($subMatches as $match) {
try {
$subParser = $subTemplate->getParser($match);
} catch (Logic $ex) {
throw new SubDataInvalidFormat($placeholder, $ex->getMessage(), $ex);
}
if ($subType = $subParser->getType()) {
throw new SubDataInvalidFormat($placeholder, 'Only modifiers can be used. Found type: ' . $subType);
}
$elementModifiers[$subTemplate->currentName] = $subParser->getModifiers();
}

return $elementModifiers;
}

/**
* Get parser object
*
* @param array $matches
* @return ParserPH
* @throws \go\DB\Exceptions\Templater
*/
protected function getParser($matches)
{
if (isset($matches[3])) {
$name = $matches[3];
if (empty($name)) {
if (empty($name) && $matches[2] == ':') {
/* There is a named placeholder without name ("?set:") */
throw new UnknownPlaceholder($matches[0]);
}
} else {
$name = null;
if ($placeholder == '?') { // "??" for question mark
return '?';
}
}
if ($name) {
if ($this->counter == 0) {
Expand All @@ -116,26 +177,16 @@ protected function placeholderClb($matches)
/* There is a named placeholder although already used regular */
throw new MixedPlaceholder($matches[0]);
}
if (!array_key_exists($name, $this->data)) {
throw new DataNamed($name);
}
$value = $this->data[$name];
$this->currentName = $name;
} elseif ($this->named) {
/* There is a regular placeholder although already used named */
throw new MixedPlaceholder($matches[0]);
} else {
if (!array_key_exists($this->counter, $this->data)) {
/* Data for regular placeholders is ended */
throw new DataNotEnough(count($this->data), $this->counter);
}
$value = $this->data[$this->counter];
$this->currentName = $this->counter;
}
$this->counter++;
$parser = new ParserPH($placeholder);
$type = $parser->getType();
$modifiers = $parser->getModifiers();
$method = 'replacement'.strtoupper($type);
return $this->$method($value, $modifiers);

return new ParserPH(isset($matches[1]) ? $matches[1] : '');
}

/**
Expand Down Expand Up @@ -198,9 +249,10 @@ protected function replacement($value, array $modifiers)
*
* @param array $value
* @param array $modifiers
* @param array $elementModifiers
* @return string
*/
protected function replacementL($value, array $modifiers)
protected function replacementL($value, array $modifiers, array $elementModifiers = [])
{
if (!is_array($value)) {
throw new DataInvalidFormat('list', 'required array (list of values)');
Expand All @@ -210,7 +262,15 @@ protected function replacementL($value, array $modifiers)
if (is_array($element)) {
throw new DataInvalidFormat('list', 'required scalar in item #'.$k);
}
$values[] = $this->valueModification($element, $modifiers);
if (!empty($elementModifiers)) {
if (!isset($elementModifiers[$k])) {
throw new DataInvalidFormat('list', 'No modifier for key: ' . $k);
}
$elementMod = $elementModifiers[$k];
} else {
$elementMod = $modifiers;
}
$values[] = $this->valueModification($element, $elementMod);
}
return implode(', ', $values);
}
Expand All @@ -220,9 +280,10 @@ protected function replacementL($value, array $modifiers)
*
* @param array $value
* @param array $modifiers
* @param array $elementModifiers
* @return string
*/
protected function replacementS($value, array $modifiers)
protected function replacementS($value, array $modifiers, array $elementModifiers = [])
{
if (!is_array($value)) {
throw new DataInvalidFormat('set', 'required array (column => value)');
Expand All @@ -237,14 +298,22 @@ protected function replacementS($value, array $modifiers)
$element = $this->replacementC($element, $modifiers);
}
} else {
if (is_int($element)) {
$element = $this->implementation->reprInt($this->connection, $element);
if (!empty($elementModifiers)) {
if (!isset($elementModifiers[$col])) {
throw new DataInvalidFormat('set', 'No modifier for col: ' . $col);
}
$element = $this->valueModification($element, $elementModifiers[$col]);
} else {
$element = $this->valueModification($element, $modifiers);
if (is_int($element)) {
$element = $this->implementation->reprInt($this->connection, $element);
} else {
$element = $this->valueModification($element, $modifiers);
}
}
}
$set[] = $key.'='.$element;
}

return implode(', ', $set);
}

Expand All @@ -253,16 +322,17 @@ protected function replacementS($value, array $modifiers)
*
* @param array $value
* @param array $modifiers
* @param array $elementModifiers
* @return string
*/
private function replacementV($value, array $modifiers)
private function replacementV($value, array $modifiers, array $elementModifiers = [])
{
if (!is_array($value)) {
throw new DataInvalidFormat('values', 'required array of arrays');
}
$values = array();
foreach ($value as $v) {
$values[] = '('.$this->replacementL($v, $modifiers).')';
$values[] = '('.$this->replacementL($v, $modifiers, $elementModifiers).')';
}
return implode(', ', $values);
}
Expand Down Expand Up @@ -541,6 +611,11 @@ private function whereGroup($value, array $modifiers, $sep = 'AND')
*/
protected $data;

/**
* @var string
*/
protected $currentName = '';

/**
* @var string
*/
Expand Down
58 changes: 58 additions & 0 deletions tests/Helpers/Templater/ExceptionsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -236,4 +236,62 @@ public function providerExceptionDataInvalidFormat()
],
];
}

/**
* @dataProvider providerExceptionSubDataInvalidFormat
* @param string $placeholder
* @param mixed $data
* @param string $exceptionClass
* @param string $message
*/
public function testExceptionSubDataInvalidFormat($placeholder, $data, $exceptionClass, $message)
{
$pattern = '?'.$placeholder;
$data = [$data];
$templater = $this->createTemplater($pattern, $data);
$this->setExpectedException($exceptionClass, $message);
$templater->parse();
}

public function providerExceptionSubDataInvalidFormat()
{
return [
'type' => [
'v[?i, ?set-int]',
[1, 2],
'go\DB\Exceptions\SubDataInvalidFormat',
'Only modifiers can be used. Found type: s',
],
'internal' => [
'l[?i, ?wtf]',
[1, 2],
'go\DB\Exceptions\SubDataInvalidFormat',
'Invalid sub data for ?l: Unknown placeholder "wtf"',
],
'mixedSubPlaceholder' => [
'l[?i, ?i:foo]',
[1, 2],
'go\DB\Exceptions\SubDataInvalidFormat',
'Invalid sub data for ?l: Mixed placeholder "?i:foo"',
],
'mixedSubPlaceholder2' => [
'v[?i:bar, ?i]',
[1, 2],
'go\DB\Exceptions\SubDataInvalidFormat',
'Invalid sub data for ?v: Mixed placeholder "?i"',
],
'listElementModifierNotSet' => [
'l[?i, ?i]',
[1, 2, 3],
'go\DB\Exceptions\DataInvalidFormat',
'Data for ?list has invalid format: "No modifier for key: 2"',
],
'setElementModifierNotSet' => [
's[?i:foo, ?i:baZ]',
['foo' => 1, 'bar' => 2],
'go\DB\Exceptions\DataInvalidFormat',
'Data for ?set has invalid format: "No modifier for col: bar"',
],
];
}
}
5 changes: 5 additions & 0 deletions tests/Helpers/Templater/ListTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ public function providerTemplater()
[$list],
'INSERT INTO `table` VALUES (0, 1, NULL, 3)',
],
'subtype' => [
'INSERT INTO `table` VALUES (?l[?int, ?string, ?n, ?int])',
[$list],
'INSERT INTO `table` VALUES (0, "1", NULL, 3)',
],
];
}
}
5 changes: 5 additions & 0 deletions tests/Helpers/Templater/SetTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ public function providerTemplater()
[$set],
'INSERT INTO `table` SET `s`="str\"ing", `d`="3.5", `n`=NULL',
],
'subset' => [
'INSERT INTO `table` SET ?s[?string:s, ?int:d, ?null:n]',
[$set],
'INSERT INTO `table` SET `s`="str\"ing", `d`=3, `n`=NULL',
],
'null' => [
'INSERT INTO `table` SET ?set-null',
[$set],
Expand Down
5 changes: 5 additions & 0 deletions tests/Helpers/Templater/ValuesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ public function providerTemplater()
[$values],
'INSERT INTO `table` VALUES (0, 1, 2), ("one", NULL, "three")',
],
'subset' => [
'INSERT INTO `table` VALUES ?values[?string, ?int-null, ?i]',
[$values],
'INSERT INTO `table` VALUES ("0", 1, 2), ("one", NULL, 0)',
],
'null' => [
'INSERT INTO `table` VALUES ?vn',
[$values],
Expand Down