1: <?php
2: /**
3: * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
4: * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
5: *
6: * Licensed under The MIT License
7: * For full copyright and license information, please see the LICENSE.txt
8: * Redistributions of files must retain the above copyright notice.
9: *
10: * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
11: * @link https://cakephp.org CakePHP(tm) Project
12: * @since 3.2.0
13: * @license https://opensource.org/licenses/mit-license.php MIT License
14: */
15: namespace Cake\Database;
16:
17: use Cake\Database\Type;
18: use Cake\Database\Type\BatchCastingInterface;
19: use Cake\Database\Type\OptionalConvertInterface;
20:
21: /**
22: * A callable class to be used for processing each of the rows in a statement
23: * result, so that the values are converted to the right PHP types.
24: */
25: class FieldTypeConverter
26: {
27: /**
28: * An array containing the name of the fields and the Type objects
29: * each should use when converting them.
30: *
31: * @var array
32: */
33: protected $_typeMap;
34:
35: /**
36: * An array containing the name of the fields and the Type objects
37: * each should use when converting them using batching.
38: *
39: * @var array
40: */
41: protected $batchingTypeMap;
42:
43: /**
44: * An array containing all the types registered in the Type system
45: * at the moment this object is created. Used so that the types list
46: * is not fetched on each single row of the results.
47: *
48: * @var array
49: */
50: protected $types;
51:
52: /**
53: * The driver object to be used in the type conversion
54: *
55: * @var \Cake\Database\Driver
56: */
57: protected $_driver;
58:
59: /**
60: * Builds the type map
61: *
62: * @param \Cake\Database\TypeMap $typeMap Contains the types to use for converting results
63: * @param \Cake\Database\Driver $driver The driver to use for the type conversion
64: */
65: public function __construct(TypeMap $typeMap, Driver $driver)
66: {
67: $this->_driver = $driver;
68: $map = $typeMap->toArray();
69: $types = Type::buildAll();
70:
71: $simpleMap = $batchingMap = [];
72: $simpleResult = $batchingResult = [];
73:
74: foreach ($types as $k => $type) {
75: if ($type instanceof OptionalConvertInterface && !$type->requiresToPhpCast()) {
76: continue;
77: }
78:
79: // Because of backwards compatibility reasons, we won't allow classes
80: // inheriting Type in userland code to be batchable, even if they implement
81: // the interface. Users can implement the TypeInterface instead to have
82: // access to this feature.
83: $batchingType = $type instanceof BatchCastingInterface &&
84: !($type instanceof Type &&
85: strpos(get_class($type), 'Cake\Database\Type') === false);
86:
87: if ($batchingType) {
88: $batchingMap[$k] = $type;
89: continue;
90: }
91:
92: $simpleMap[$k] = $type;
93: }
94:
95: foreach ($map as $field => $type) {
96: if (isset($simpleMap[$type])) {
97: $simpleResult[$field] = $simpleMap[$type];
98: continue;
99: }
100: if (isset($batchingMap[$type])) {
101: $batchingResult[$type][] = $field;
102: }
103: }
104:
105: // Using batching when there is only a couple for the type is actually slower,
106: // so, let's check for that case here.
107: foreach ($batchingResult as $type => $fields) {
108: if (count($fields) > 2) {
109: continue;
110: }
111:
112: foreach ($fields as $f) {
113: $simpleResult[$f] = $batchingMap[$type];
114: }
115: unset($batchingResult[$type]);
116: }
117:
118: $this->types = $types;
119: $this->_typeMap = $simpleResult;
120: $this->batchingTypeMap = $batchingResult;
121: }
122:
123: /**
124: * Converts each of the fields in the array that are present in the type map
125: * using the corresponding Type class.
126: *
127: * @param array $row The array with the fields to be casted
128: * @return array
129: */
130: public function __invoke($row)
131: {
132: if (!empty($this->_typeMap)) {
133: foreach ($this->_typeMap as $field => $type) {
134: $row[$field] = $type->toPHP($row[$field], $this->_driver);
135: }
136: }
137:
138: if (!empty($this->batchingTypeMap)) {
139: foreach ($this->batchingTypeMap as $t => $fields) {
140: $row = $this->types[$t]->manyToPHP($row, $fields, $this->_driver);
141: }
142: }
143:
144: return $row;
145: }
146: }
147: