Source for file DibiTranslator.php

Documentation is available at DibiTranslator.php

  1. 1: <?php
  2. 2:  
  3. 3: /**
  4. 4:  * dibi - tiny'n'smart database abstraction layer
  5. 5:  * ----------------------------------------------
  6. 6:  *
  7. 7:  * @copyright  Copyright (c) 2005, 2010 David Grudl
  8. 8:  * @license    http://dibiphp.com/license  dibi license
  9. 9:  * @link       http://dibiphp.com
  10. 10:  * @package    dibi
  11. 11:  */
  12. 12:  
  13. 13:  
  14. 14:  
  15. 15: /**
  16. 16:  * dibi SQL translator.
  17. 17:  *
  18. 18:  * @copyright  Copyright (c) 2005, 2010 David Grudl
  19. 19:  * @package    dibi
  20. 20:  */
  21. 21: final class DibiTranslator extends DibiObject
  22. 22: {
  23. 23:     /** @var IDibiDriver */
  24. 24:     private $driver;
  25. 25:  
  26. 26:     /** @var int */
  27. 27:     private $cursor;
  28. 28:  
  29. 29:     /** @var array */
  30. 30:     private $args;
  31. 31:  
  32. 32:     /** @var bool */
  33. 33:     private $hasError;
  34. 34:  
  35. 35:     /** @var bool */
  36. 36:     private $comment;
  37. 37:  
  38. 38:     /** @var int */
  39. 39:     private $ifLevel;
  40. 40:  
  41. 41:     /** @var int */
  42. 42:     private $ifLevelStart;
  43. 43:  
  44. 44:     /** @var int */
  45. 45:     private $limit;
  46. 46:  
  47. 47:     /** @var int */
  48. 48:     private $offset;
  49. 49:  
  50. 50:     /** @var DibiLazyStorage */
  51. 51:     private $identifiers;
  52. 52:  
  53. 53:  
  54. 54:  
  55. 55:     public function __construct(IDibiDriver $driver)
  56. 56:     {
  57. 57:         $this->driver $driver;
  58. 58:     }
  59. 59:  
  60. 60:  
  61. 61:  
  62. 62:     /**
  63. 63:      * Generates SQL.
  64. 64:      * @param  array 
  65. 65:      * @return string 
  66. 66:      * @throws DibiException
  67. 67:      */
  68. 68:     public function translate(array $args)
  69. 69:     {
  70. 70:         $this->identifiers new DibiLazyStorage(array($this'delimite'));
  71. 71:         $args array_values($args);
  72. 72:         while (count($args=== && is_array($args[0])) // implicit array expansion
  73. 73:             $args array_values($args[0]);
  74. 74:         }
  75. 75:         $this->args $args;
  76. 76:  
  77. 77:         $this->limit = -1;
  78. 78:         $this->offset 0;
  79. 79:         $this->hasError FALSE;
  80. 80:         $commandIns NULL;
  81. 81:         $lastArr NULL;
  82. 82:         // shortcuts
  83. 83:         $cursor $this->cursor;
  84. 84:         $cursor 0;
  85. 85:  
  86. 86:         // conditional sql
  87. 87:         $this->ifLevel $this->ifLevelStart 0;
  88. 88:         $comment $this->comment;
  89. 89:         $comment FALSE;
  90. 90:  
  91. 91:         // iterate
  92. 92:         $sql array();
  93. 93:         while ($cursor count($this->args))
  94. 94:         {
  95. 95:             $arg $this->args[$cursor];
  96. 96:             $cursor++;
  97. 97:  
  98. 98:             // simple string means SQL
  99. 99:             if (is_string($arg)) {
  100. 100:                 // speed-up - is regexp required?
  101. 101:                 $toSkip strcspn($arg'`[\'":%');
  102. 102:  
  103. 103:                 if (strlen($arg=== $toSkip// needn't be translated
  104. 104:                     $sql[$arg;
  105. 105:                 else {
  106. 106:                     $sql[substr($arg0$toSkip)
  107. 107: /*
  108. 108:                     . preg_replace_callback('/
  109. 109:                     (?=[`[\'":%?])                    ## speed-up
  110. 110:                     (?:
  111. 111:                         `(.+?)`|                     ## 1) `identifier`
  112. 112:                         \[(.+?)\]|                   ## 2) [identifier]
  113. 113:                         (\')((?:\'\'|[^\'])*)\'|     ## 3,4) 'string'
  114. 114:                         (")((?:""|[^"])*)"|          ## 5,6) "string"
  115. 115:                         (\'|")|                      ## 7) lone quote
  116. 116:                         :(\S*?:)([a-zA-Z0-9._]?)|    ## 8,9) :substitution:
  117. 117:                         %([a-zA-Z~][a-zA-Z0-9~]{0,5})|## 10) modifier
  118. 118:                         (\?)                         ## 11) placeholder
  119. 119:                     )/xs',
  120. 120: */                  // note: this can change $this->args & $this->cursor & ...
  121. 121:                     . preg_replace_callback('/(?=[`[\'":%?])(?:`(.+?)`|\[(.+?)\]|(\')((?:\'\'|[^\'])*)\'|(")((?:""|[^"])*)"|(\'|")|:(\S*?:)([a-zA-Z0-9._]?)|%([a-zA-Z~][a-zA-Z0-9~]{0,5})|(\?))/s',
  122. 122:                             array($this'cb'),
  123. 123:                             substr($arg$toSkip)
  124. 124:                     );
  125. 125:                     if (preg_last_error()) throw new PcreException;
  126. 126:                 }
  127. 127:                 continue;
  128. 128:             }
  129. 129:  
  130. 130:             if ($comment{
  131. 131:                 $sql['...';
  132. 132:                 continue;
  133. 133:             }
  134. 134:  
  135. 135:             if ($arg instanceof Traversable{
  136. 136:                 $arg iterator_to_array($arg);
  137. 137:             }
  138. 138:  
  139. 139:             if (is_array($arg)) {
  140. 140:                 if (is_string(key($arg))) {
  141. 141:                     // associative array -> autoselect between SET or VALUES & LIST
  142. 142:                     if ($commandIns === NULL{
  143. 143:                         $commandIns strtoupper(substr(ltrim($this->args[0])06));
  144. 144:                         $commandIns $commandIns === 'INSERT' || $commandIns === 'REPLAC';
  145. 145:                         $sql[$this->formatValue($arg$commandIns 'v' 'a');
  146. 146:                     else {
  147. 147:                         if ($lastArr === $cursor 1$sql[',';
  148. 148:                         $sql[$this->formatValue($arg$commandIns 'l' 'a');
  149. 149:                     }
  150. 150:                     $lastArr $cursor;
  151. 151:                     continue;
  152. 152:                 }
  153. 153:             }
  154. 154:  
  155. 155:             // default processing
  156. 156:             $sql[$this->formatValue($argFALSE);
  157. 157:         // while
  158. 158:  
  159. 159:  
  160. 160:         if ($comment$sql["*/";
  161. 161:  
  162. 162:         $sql implode(' '$sql);
  163. 163:  
  164. 164:         if ($this->hasError{
  165. 165:             throw new DibiException('SQL translate error'0$sql);
  166. 166:         }
  167. 167:  
  168. 168:         // apply limit
  169. 169:         if ($this->limit > -|| $this->offset 0{
  170. 170:             $this->driver->applyLimit($sql$this->limit$this->offset);
  171. 171:         }
  172. 172:  
  173. 173:         return $sql;
  174. 174:     }
  175. 175:  
  176. 176:  
  177. 177:  
  178. 178:     /**
  179. 179:      * Apply modifier to single value.
  180. 180:      * @param  mixed 
  181. 181:      * @param  string 
  182. 182:      * @return string 
  183. 183:      */
  184. 184:     public function formatValue($value$modifier)
  185. 185:     {
  186. 186:         // array processing (with or without modifier)
  187. 187:         if ($value instanceof Traversable{
  188. 188:             $value iterator_to_array($value);
  189. 189:         }
  190. 190:  
  191. 191:         if (is_array($value)) {
  192. 192:             $vx $kx array();
  193. 193:             switch ($modifier{
  194. 194:             case 'and':
  195. 195:             case 'or':  // key=val AND key IS NULL AND ...
  196. 196:                 if (empty($value)) {
  197. 197:                     return '1=1';
  198. 198:                 }
  199. 199:  
  200. 200:                 foreach ($value as $k => $v{
  201. 201:                     if (is_string($k)) {
  202. 202:                         $pair explode('%'$k2)// split into identifier & modifier
  203. 203:                         $k $this->identifiers->{$pair[0]' ';
  204. 204:                         if (!isset($pair[1])) {
  205. 205:                             $v $this->formatValue($vFALSE);
  206. 206:                             $vx[$k ($v === 'NULL' 'IS ' '= '$v;
  207. 207:  
  208. 208:                         elseif ($pair[1=== 'ex'// TODO: this will be removed
  209. 209:                             $vx[$k $this->formatValue($v'ex');
  210. 210:  
  211. 211:                         else {
  212. 212:                             $v $this->formatValue($v$pair[1]);
  213. 213:                             $vx[$k ($pair[1=== 'l' || $pair[1=== 'in' 'IN ' ($v === 'NULL' 'IS ' '= ')) $v;
  214. 214:                         }
  215. 215:  
  216. 216:                     else {
  217. 217:                         $vx[$this->formatValue($v'ex');
  218. 218:                     }
  219. 219:                 }
  220. 220:                 return '(' implode(') ' strtoupper($modifier' ('$vx')';
  221. 221:  
  222. 222:             case 'n':  // key, key, ... identifier names
  223. 223:                 foreach ($value as $k => $v{
  224. 224:                     if (is_string($k)) {
  225. 225:                         $vx[$this->identifiers->$k (empty($v'' ' AS ' $v);
  226. 226:                     else {
  227. 227:                         $pair explode('%'$v2)// split into identifier & modifier
  228. 228:                         $vx[$this->identifiers->{$pair[0]};
  229. 229:                     }
  230. 230:                 }
  231. 231:                 return implode(', '$vx);
  232. 232:  
  233. 233:  
  234. 234:             case 'a'// key=val, key=val, ...
  235. 235:                 foreach ($value as $k => $v{
  236. 236:                     $pair explode('%'$k2)// split into identifier & modifier
  237. 237:                     $vx[$this->identifiers->{$pair[0]'='
  238. 238:                         . $this->formatValue($visset($pair[1]$pair[1(is_array($v'ex' FALSE));
  239. 239:                 }
  240. 240:                 return implode(', '$vx);
  241. 241:  
  242. 242:  
  243. 243:             case 'in':// replaces scalar %in modifier!
  244. 244:             case 'l'// (val, val, ...)
  245. 245:                 foreach ($value as $k => $v{
  246. 246:                     $pair explode('%'$k2)// split into identifier & modifier
  247. 247:                     $vx[$this->formatValue($visset($pair[1]$pair[1(is_array($v'ex' FALSE));
  248. 248:                 }
  249. 249:                 return '(' (($vx || $modifier === 'l'implode(', '$vx'NULL'')';
  250. 250:  
  251. 251:  
  252. 252:             case 'v'// (key, key, ...) VALUES (val, val, ...)
  253. 253:                 foreach ($value as $k => $v{
  254. 254:                     $pair explode('%'$k2)// split into identifier & modifier
  255. 255:                     $kx[$this->identifiers->{$pair[0]};
  256. 256:                     $vx[$this->formatValue($visset($pair[1]$pair[1(is_array($v'ex' FALSE));
  257. 257:                 }
  258. 258:                 return '(' implode(', '$kx') VALUES (' implode(', '$vx')';
  259. 259:  
  260. 260:             case 'm'// (key, key, ...) VALUES (val, val, ...), (val, val, ...), ...
  261. 261:                 foreach ($value as $k => $v{
  262. 262:                     if (is_array($v)) {
  263. 263:                         if (isset($proto)) {
  264. 264:                             if ($proto !== array_keys($v)) {
  265. 265:                                 $this->hasError TRUE;
  266. 266:                                 return '**Multi-insert array "' $k '" is different.**';
  267. 267:                             }
  268. 268:                         else {
  269. 269:                             $proto array_keys($v);
  270. 270:                         }
  271. 271:                     else {
  272. 272:                         $this->hasError TRUE;
  273. 273:                         return '**Unexpected type ' gettype($v'**';
  274. 274:                     }
  275. 275:  
  276. 276:                     $pair explode('%'$k2)// split into identifier & modifier
  277. 277:                     $kx[$this->identifiers->{$pair[0]};
  278. 278:                     foreach ($v as $k2 => $v2{
  279. 279:                         $vx[$k2][$this->formatValue($v2isset($pair[1]$pair[1(is_array($v2'ex' FALSE));
  280. 280:                     }
  281. 281:                 }
  282. 282:                 foreach ($vx as $k => $v{
  283. 283:                     $vx[$k'(' implode(', '$v')';
  284. 284:                 }
  285. 285:                 return '(' implode(', '$kx') VALUES ' implode(', '$vx);
  286. 286:  
  287. 287:             case 'by'// key ASC, key DESC
  288. 288:                 foreach ($value as $k => $v{
  289. 289:                     if (is_array($v)) {
  290. 290:                         $vx[$this->formatValue($v'ex');
  291. 291:                     elseif (is_string($k)) {
  292. 292:                         $v (is_string($v&& strncasecmp($v'd'1)) || $v 'ASC' 'DESC';
  293. 293:                         $vx[$this->identifiers->$k ' ' $v;
  294. 294:                     else {
  295. 295:                         $vx[$this->identifiers->$v;
  296. 296:                     }
  297. 297:                 }
  298. 298:                 return implode(', '$vx);
  299. 299:  
  300. 300:             case 'ex':
  301. 301:             case 'sql':
  302. 302:                 $translator new self($this->driver);
  303. 303:                 return $translator->translate($value);
  304. 304:  
  305. 305:             default:  // value, value, value - all with the same modifier
  306. 306:                 foreach ($value as $v{
  307. 307:                     $vx[$this->formatValue($v$modifier);
  308. 308:                 }
  309. 309:                 return implode(', '$vx);
  310. 310:             }
  311. 311:         }
  312. 312:  
  313. 313:  
  314. 314:         // with modifier procession
  315. 315:         if ($modifier{
  316. 316:             if ($value !== NULL && !is_scalar($value&& !($value instanceof DateTime)) {  // array is already processed
  317. 317:                 $this->hasError TRUE;
  318. 318:                 return '**Unexpected type ' gettype($value'**';
  319. 319:             }
  320. 320:  
  321. 321:             switch ($modifier{
  322. 322:             case 's':  // string
  323. 323:             case 'bin':// binary
  324. 324:             case 'b':  // boolean
  325. 325:                 return $value === NULL 'NULL' $this->driver->escape($value$modifier);
  326. 326:  
  327. 327:             case 'sN'// string or NULL
  328. 328:             case 'sn':
  329. 329:                 return $value == '' 'NULL' $this->driver->escape($valuedibi::TEXT)// notice two equal signs
  330. 330:  
  331. 331:             case 'iN'// signed int or NULL
  332. 332:             case 'in'// deprecated
  333. 333:                 if ($value == ''$value NULL;
  334. 334:                 // intentionally break omitted
  335. 335:  
  336. 336:             case 'i':  // signed int
  337. 337:             case 'u':  // unsigned int, ignored
  338. 338:                 // support for long numbers - keep them unchanged
  339. 339:                 if (is_string($value&& preg_match('#[+-]?\d++(e\d+)?$#A'$value)) {
  340. 340:                     return $value;
  341. 341:                 else {
  342. 342:                     return $value === NULL 'NULL' : (string) (int) ($value 0);
  343. 343:                 }
  344. 344:  
  345. 345:             case 'f':  // float
  346. 346:                 // support for extreme numbers - keep them unchanged
  347. 347:                 if (is_string($value&& is_numeric($value&& strpos($value'x'=== FALSE{
  348. 348:                     return $value// something like -9E-005 is accepted by SQL, HEX values are not
  349. 349:                 else {
  350. 350:                     return $value === NULL 'NULL' rtrim(rtrim(number_format($value 05'.''')'0')'.');
  351. 351:                 }
  352. 352:  
  353. 353:             case 'd':  // date
  354. 354:             case 't':  // datetime
  355. 355:                 if ($value === NULL{
  356. 356:                     return 'NULL';
  357. 357:                 else {
  358. 358:                     if (is_numeric($value)) {
  359. 359:                         $value = (int) $value// timestamp
  360. 360:  
  361. 361:                     elseif (is_string($value)) {
  362. 362:                         $value new DateTime($value);
  363. 363:                     }
  364. 364:                     return $this->driver->escape($value$modifier);
  365. 365:                 }
  366. 366:  
  367. 367:             case 'by':
  368. 368:             case 'n':  // identifier name
  369. 369:                 return $this->identifiers->$value;
  370. 370:  
  371. 371:             case 'ex':
  372. 372:             case 'sql'// preserve as dibi-SQL  (TODO: leave only %ex)
  373. 373:                 $value = (string) $value;
  374. 374:                 // speed-up - is regexp required?
  375. 375:                 $toSkip strcspn($value'`[\'":');
  376. 376:                 if (strlen($value!== $toSkip{
  377. 377:                     $value substr($value0$toSkip)
  378. 378:                     . preg_replace_callback(
  379. 379:                         '/(?=[`[\'":])(?:`(.+?)`|\[(.+?)\]|(\')((?:\'\'|[^\'])*)\'|(")((?:""|[^"])*)"|(\'|")|:(\S*?:)([a-zA-Z0-9._]?))/s',
  380. 380:                         array($this'cb'),
  381. 381:                         substr($value$toSkip)
  382. 382:                     );
  383. 383:                     if (preg_last_error()) throw new PcreException;
  384. 384:                 }
  385. 385:                 return $value;
  386. 386:  
  387. 387:             case 'SQL'// preserve as real SQL (TODO: rename to %sql)
  388. 388:                 return (string) $value;
  389. 389:  
  390. 390:             case 'like~':  // LIKE string%
  391. 391:                 return $this->driver->escapeLike($value1);
  392. 392:  
  393. 393:             case '~like':  // LIKE %string
  394. 394:                 return $this->driver->escapeLike($value-1);
  395. 395:  
  396. 396:             case '~like~'// LIKE %string%
  397. 397:                 return $this->driver->escapeLike($value0);
  398. 398:  
  399. 399:             case 'and':
  400. 400:             case 'or':
  401. 401:             case 'a':
  402. 402:             case 'l':
  403. 403:             case 'v':
  404. 404:                 $this->hasError TRUE;
  405. 405:                 return '**Unexpected type ' gettype($value'**';
  406. 406:  
  407. 407:             default:
  408. 408:                 $this->hasError TRUE;
  409. 409:                 return "**Unknown or invalid modifier %$modifier**";
  410. 410:             }
  411. 411:         }
  412. 412:  
  413. 413:  
  414. 414:         // without modifier procession
  415. 415:         if (is_string($value)) {
  416. 416:             return $this->driver->escape($valuedibi::TEXT);
  417. 417:  
  418. 418:         elseif (is_int($value)) {
  419. 419:             return (string) $value;
  420. 420:  
  421. 421:         elseif (is_float($value)) {
  422. 422:             return rtrim(rtrim(number_format($value5'.''')'0')'.');
  423. 423:  
  424. 424:         elseif (is_bool($value)) {
  425. 425:             return $this->driver->escape($valuedibi::BOOL);
  426. 426:  
  427. 427:         elseif ($value === NULL{
  428. 428:             return 'NULL';
  429. 429:  
  430. 430:         elseif ($value instanceof DateTime{
  431. 431:             return $this->driver->escape($valuedibi::DATETIME);
  432. 432:  
  433. 433:         else {
  434. 434:             $this->hasError TRUE;
  435. 435:             return '**Unexpected ' gettype($value'**';
  436. 436:         }
  437. 437:     }
  438. 438:  
  439. 439:  
  440. 440:  
  441. 441:     /**
  442. 442:      * PREG callback from translate() or formatValue().
  443. 443:      * @param  array 
  444. 444:      * @return string 
  445. 445:      */
  446. 446:     private function cb($matches)
  447. 447:     {
  448. 448:         //    [1] => `ident`
  449. 449:         //    [2] => [ident]
  450. 450:         //    [3] => '
  451. 451:         //    [4] => string
  452. 452:         //    [5] => "
  453. 453:         //    [6] => string
  454. 454:         //    [7] => lone-quote
  455. 455:         //    [8] => substitution
  456. 456:         //    [9] => substitution flag
  457. 457:         //    [10] => modifier (when called from self::translate())
  458. 458:         //    [11] => placeholder (when called from self::translate())
  459. 459:  
  460. 460:  
  461. 461:         if (!empty($matches[11])) // placeholder
  462. 462:             $cursor $this->cursor;
  463. 463:  
  464. 464:             if ($cursor >= count($this->args)) {
  465. 465:                 $this->hasError TRUE;
  466. 466:                 return "**Extra placeholder**";
  467. 467:             }
  468. 468:  
  469. 469:             $cursor++;
  470. 470:             return $this->formatValue($this->args[$cursor 1]FALSE);
  471. 471:         }
  472. 472:  
  473. 473:         if (!empty($matches[10])) // modifier
  474. 474:             $mod $matches[10];
  475. 475:             $cursor $this->cursor;
  476. 476:  
  477. 477:             if ($cursor >= count($this->args&& $mod !== 'else' && $mod !== 'end'{
  478. 478:                 $this->hasError TRUE;
  479. 479:                 return "**Extra modifier %$mod**";
  480. 480:             }
  481. 481:  
  482. 482:             if ($mod === 'if'{
  483. 483:                 $this->ifLevel++;
  484. 484:                 $cursor++;
  485. 485:                 if (!$this->comment && !$this->args[$cursor 1]{
  486. 486:                     // open comment
  487. 487:                     $this->ifLevelStart $this->ifLevel;
  488. 488:                     $this->comment TRUE;
  489. 489:                     return "/*";
  490. 490:                 }
  491. 491:                 return '';
  492. 492:  
  493. 493:             elseif ($mod === 'else'{
  494. 494:                 if ($this->ifLevelStart === $this->ifLevel{
  495. 495:                     $this->ifLevelStart 0;
  496. 496:                     $this->comment FALSE;
  497. 497:                     return "*/";
  498. 498:                 elseif (!$this->comment{
  499. 499:                     $this->ifLevelStart $this->ifLevel;
  500. 500:                     $this->comment TRUE;
  501. 501:                     return "/*";
  502. 502:                 }
  503. 503:  
  504. 504:             elseif ($mod === 'end'{
  505. 505:                 $this->ifLevel--;
  506. 506:                 if ($this->ifLevelStart === $this->ifLevel 1{
  507. 507:                     // close comment
  508. 508:                     $this->ifLevelStart 0;
  509. 509:                     $this->comment FALSE;
  510. 510:                     return "*/";
  511. 511:                 }
  512. 512:                 return '';
  513. 513:  
  514. 514:             elseif ($mod === 'ex'// array expansion
  515. 515:                 array_splice($this->args$cursor1$this->args[$cursor]);
  516. 516:                 return '';
  517. 517:  
  518. 518:             elseif ($mod === 'lmt'// apply limit
  519. 519:                 if ($this->args[$cursor!== NULL$this->limit = (int) $this->args[$cursor];
  520. 520:                 $cursor++;
  521. 521:                 return '';
  522. 522:  
  523. 523:             elseif ($mod === 'ofs'// apply offset
  524. 524:                 if ($this->args[$cursor!== NULL$this->offset = (int) $this->args[$cursor];
  525. 525:                 $cursor++;
  526. 526:                 return '';
  527. 527:  
  528. 528:             else // default processing
  529. 529:                 $cursor++;
  530. 530:                 return $this->formatValue($this->args[$cursor 1]$mod);
  531. 531:             }
  532. 532:         }
  533. 533:  
  534. 534:         if ($this->commentreturn '...';
  535. 535:  
  536. 536:         if ($matches[1])  // SQL identifiers: `ident`
  537. 537:             return $this->identifiers->{$matches[1]};
  538. 538:  
  539. 539:         if ($matches[2])  // SQL identifiers: [ident]
  540. 540:             return $this->identifiers->{$matches[2]};
  541. 541:  
  542. 542:         if ($matches[3])  // SQL strings: '...'
  543. 543:             return $this->driver->escapestr_replace("''""'"$matches[4])dibi::TEXT);
  544. 544:  
  545. 545:         if ($matches[5])  // SQL strings: "..."
  546. 546:             return $this->driver->escapestr_replace('""''"'$matches[6])dibi::TEXT);
  547. 547:  
  548. 548:         if ($matches[7]// string quote
  549. 549:             $this->hasError TRUE;
  550. 550:             return '**Alone quote**';
  551. 551:         }
  552. 552:  
  553. 553:         if ($matches[8]// SQL identifier substitution
  554. 554:             $m substr($matches[8]0-1);
  555. 555:             return $matches[9== '' $this->formatValue(dibi::$substs->$mFALSEdibi::$substs->$m $matches[9]// value or identifier
  556. 556:         }
  557. 557:  
  558. 558:         die('this should be never executed');
  559. 559:     }
  560. 560:  
  561. 561:  
  562. 562:  
  563. 563:     /**
  564. 564:      * Apply substitutions to indentifier and delimites it.
  565. 565:      * @param  string indentifier
  566. 566:      * @return string 
  567. 567:      * @ignore internal
  568. 568:      */
  569. 569:     public function delimite($value)
  570. 570:     {
  571. 571:         $value self::substitute($value);
  572. 572:         $parts explode('.'$value);
  573. 573:         foreach ($parts as $v{
  574. 574:             if ($v !== '*'$v $this->driver->escape($vdibi::IDENTIFIER);
  575. 575:         }
  576. 576:         return implode('.'$parts);
  577. 577:     }
  578. 578:  
  579. 579:  
  580. 580:  
  581. 581:     /**
  582. 582:      * Provides substitution.
  583. 583:      * @return string 
  584. 584:      */
  585. 585:     public static function substitute($value)
  586. 586:     {
  587. 587:         if (strpos($value':'!== FALSE// provide substitution
  588. 588:             return preg_replace_callback('#:([^:\s]*):#'array(__CLASS__'subCb')$value);
  589. 589:         }
  590. 590:         return $value;
  591. 591:     }
  592. 592:  
  593. 593:  
  594. 594:  
  595. 595:     /**
  596. 596:      * Substitution callback.
  597. 597:      * @param  array 
  598. 598:      * @return string 
  599. 599:      */
  600. 600:     private static function subCb($m)
  601. 601:     {
  602. 602:         return dibi::$substs->{$m[1]};
  603. 603:     }
  604. 604: