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: /**
29: * An array containing the name of the fields and the Type objects
30: * each should use when converting them.
31: *
32: * @var array
33: */
34: protected $_typeMap;
35:
36: /**
37: * An array containing the name of the fields and the Type objects
38: * each should use when converting them using batching.
39: *
40: * @var array
41: */
42: protected $batchingTypeMap;
43:
44: /**
45: * An array containing all the types registered in the Type system
46: * at the moment this object is created. Used so that the types list
47: * is not fetched on each single row of the results.
48: *
49: * @var array
50: */
51: protected $types;
52:
53: /**
54: * The driver object to be used in the type conversion
55: *
56: * @var \Cake\Database\Driver
57: */
58: protected $_driver;
59:
60: /**
61: * Builds the type map
62: *
63: * @param \Cake\Database\TypeMap $typeMap Contains the types to use for converting results
64: * @param \Cake\Database\Driver $driver The driver to use for the type conversion
65: */
66: public function __construct(TypeMap $typeMap, Driver $driver)
67: {
68: $this->_driver = $driver;
69: $map = $typeMap->toArray();
70: $types = Type::buildAll();
71:
72: $simpleMap = $batchingMap = [];
73: $simpleResult = $batchingResult = [];
74:
75: foreach ($types as $k => $type) {
76: if ($type instanceof OptionalConvertInterface && !$type->requiresToPhpCast()) {
77: continue;
78: }
79:
80: // Because of backwards compatibility reasons, we won't allow classes
81: // inheriting Type in userland code to be batchable, even if they implement
82: // the interface. Users can implement the TypeInterface instead to have
83: // access to this feature.
84: $batchingType = $type instanceof BatchCastingInterface &&
85: !($type instanceof Type &&
86: strpos(get_class($type), 'Cake\Database\Type') === false);
87:
88: if ($batchingType) {
89: $batchingMap[$k] = $type;
90: continue;
91: }
92:
93: $simpleMap[$k] = $type;
94: }
95:
96: foreach ($map as $field => $type) {
97: if (isset($simpleMap[$type])) {
98: $simpleResult[$field] = $simpleMap[$type];
99: continue;
100: }
101: if (isset($batchingMap[$type])) {
102: $batchingResult[$type][] = $field;
103: }
104: }
105:
106: // Using batching when there is only a couple for the type is actually slower,
107: // so, let's check for that case here.
108: foreach ($batchingResult as $type => $fields) {
109: if (count($fields) > 2) {
110: continue;
111: }
112:
113: foreach ($fields as $f) {
114: $simpleResult[$f] = $batchingMap[$type];
115: }
116: unset($batchingResult[$type]);
117: }
118:
119: $this->types = $types;
120: $this->_typeMap = $simpleResult;
121: $this->batchingTypeMap = $batchingResult;
122: }
123:
124: /**
125: * Converts each of the fields in the array that are present in the type map
126: * using the corresponding Type class.
127: *
128: * @param array $row The array with the fields to be casted
129: * @return array
130: */
131: public function __invoke($row)
132: {
133: if (!empty($this->_typeMap)) {
134: foreach ($this->_typeMap as $field => $type) {
135: $row[$field] = $type->toPHP($row[$field], $this->_driver);
136: }
137: }
138:
139: if (!empty($this->batchingTypeMap)) {
140: foreach ($this->batchingTypeMap as $t => $fields) {
141: $row = $this->types[$t]->manyToPHP($row, $fields, $this->_driver);
142: }
143: }
144:
145: return $row;
146: }
147: }
148: