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.0.0
13: * @license https://opensource.org/licenses/mit-license.php MIT License
14: */
15: namespace Cake\Database;
16:
17: use InvalidArgumentException;
18: use PDO;
19:
20: /**
21: * Encapsulates all conversion functions for values coming from database into PHP and
22: * going from PHP into database.
23: */
24: class Type implements TypeInterface
25: {
26:
27: /**
28: * List of supported database types. A human readable
29: * identifier is used as key and a complete namespaced class name as value
30: * representing the class that will do actual type conversions.
31: *
32: * @var string[]|\Cake\Database\Type[]
33: */
34: protected static $_types = [
35: 'tinyinteger' => 'Cake\Database\Type\IntegerType',
36: 'smallinteger' => 'Cake\Database\Type\IntegerType',
37: 'integer' => 'Cake\Database\Type\IntegerType',
38: 'biginteger' => 'Cake\Database\Type\IntegerType',
39: 'binary' => 'Cake\Database\Type\BinaryType',
40: 'binaryuuid' => 'Cake\Database\Type\BinaryUuidType',
41: 'boolean' => 'Cake\Database\Type\BoolType',
42: 'date' => 'Cake\Database\Type\DateType',
43: 'datetime' => 'Cake\Database\Type\DateTimeType',
44: 'decimal' => 'Cake\Database\Type\DecimalType',
45: 'float' => 'Cake\Database\Type\FloatType',
46: 'json' => 'Cake\Database\Type\JsonType',
47: 'string' => 'Cake\Database\Type\StringType',
48: 'text' => 'Cake\Database\Type\StringType',
49: 'time' => 'Cake\Database\Type\TimeType',
50: 'timestamp' => 'Cake\Database\Type\DateTimeType',
51: 'uuid' => 'Cake\Database\Type\UuidType',
52: ];
53:
54: /**
55: * List of basic type mappings, used to avoid having to instantiate a class
56: * for doing conversion on these.
57: *
58: * @var array
59: * @deprecated 3.1 All types will now use a specific class
60: */
61: protected static $_basicTypes = [
62: 'string' => ['callback' => [Type::class, 'strval']],
63: 'text' => ['callback' => [Type::class, 'strval']],
64: 'boolean' => [
65: 'callback' => [Type::class, 'boolval'],
66: 'pdo' => PDO::PARAM_BOOL
67: ],
68: ];
69:
70: /**
71: * Contains a map of type object instances to be reused if needed.
72: *
73: * @var \Cake\Database\Type[]
74: */
75: protected static $_builtTypes = [];
76:
77: /**
78: * Identifier name for this type
79: *
80: * @var string|null
81: */
82: protected $_name;
83:
84: /**
85: * Constructor
86: *
87: * @param string|null $name The name identifying this type
88: */
89: public function __construct($name = null)
90: {
91: $this->_name = $name;
92: }
93:
94: /**
95: * Returns a Type object capable of converting a type identified by name.
96: *
97: * @param string $name type identifier
98: * @throws \InvalidArgumentException If type identifier is unknown
99: * @return \Cake\Database\Type
100: */
101: public static function build($name)
102: {
103: if (isset(static::$_builtTypes[$name])) {
104: return static::$_builtTypes[$name];
105: }
106: if (!isset(static::$_types[$name])) {
107: throw new InvalidArgumentException(sprintf('Unknown type "%s"', $name));
108: }
109: if (is_string(static::$_types[$name])) {
110: return static::$_builtTypes[$name] = new static::$_types[$name]($name);
111: }
112:
113: return static::$_builtTypes[$name] = static::$_types[$name];
114: }
115:
116: /**
117: * Returns an arrays with all the mapped type objects, indexed by name.
118: *
119: * @return array
120: */
121: public static function buildAll()
122: {
123: $result = [];
124: foreach (static::$_types as $name => $type) {
125: $result[$name] = isset(static::$_builtTypes[$name]) ? static::$_builtTypes[$name] : static::build($name);
126: }
127:
128: return $result;
129: }
130:
131: /**
132: * Returns a Type object capable of converting a type identified by $name
133: *
134: * @param string $name The type identifier you want to set.
135: * @param \Cake\Database\Type $instance The type instance you want to set.
136: * @return void
137: */
138: public static function set($name, Type $instance)
139: {
140: static::$_builtTypes[$name] = $instance;
141: }
142:
143: /**
144: * Registers a new type identifier and maps it to a fully namespaced classname,
145: * If called with no arguments it will return current types map array
146: * If $className is omitted it will return mapped class for $type
147: *
148: * Deprecated 3.6.2:
149: * - The usage of $type as string[]|\Cake\Database\Type[] is deprecated.
150: * Use Type::setMap() with string[] instead.
151: * - Passing $className as \Cake\Database\Type instance is deprecated, use
152: * class name string only.
153: * - Using this method as getter is deprecated. Use Type::getMap() instead.
154: *
155: * @param string|string[]|\Cake\Database\Type[]|null $type If string name of type to map, if array list of arrays to be mapped
156: * @param string|\Cake\Database\Type|null $className The classname or object instance of it to register.
157: * @return array|string|null If $type is null then array with current map, if $className is null string
158: * configured class name for give $type, null otherwise
159: */
160: public static function map($type = null, $className = null)
161: {
162: if ($type === null) {
163: deprecationWarning(
164: 'Using `Type::map()` as getter is deprecated. ' .
165: 'Use `Type::getMap()` instead.'
166: );
167:
168: return static::$_types;
169: }
170: if (is_array($type)) {
171: deprecationWarning(
172: 'Using `Type::map()` to set complete types map is deprecated. ' .
173: 'Use `Type::setMap()` instead.'
174: );
175:
176: static::$_types = $type;
177:
178: return null;
179: }
180: if ($className === null) {
181: deprecationWarning(
182: 'Using `Type::map()` as getter is deprecated. ' .
183: 'Use `Type::getMap()` instead.'
184: );
185:
186: return isset(static::$_types[$type]) ? static::$_types[$type] : null;
187: }
188:
189: if (!is_string($className)) {
190: deprecationWarning(
191: 'Passing $className as object to Type::map() is deprecated. ' .
192: 'Use Type::set() instead.'
193: );
194: }
195:
196: static::$_types[$type] = $className;
197: unset(static::$_builtTypes[$type]);
198: }
199:
200: /**
201: * Set type to classname mapping.
202: *
203: * @param string[] $map List of types to be mapped.
204: * @return void
205: * @since 3.6.2
206: */
207: public static function setMap(array $map)
208: {
209: static::$_types = $map;
210: static::$_builtTypes = [];
211: }
212:
213: /**
214: * Get mapped class name or instance for type(s).
215: *
216: * @param string|null $type Type name to get mapped class for or null to get map array.
217: * @return array|string|\Cake\Database\TypeInterface|null Configured class name or instance for give $type or map array.
218: * @since 3.6.2
219: */
220: public static function getMap($type = null)
221: {
222: if ($type === null) {
223: return static::$_types;
224: }
225:
226: return isset(static::$_types[$type]) ? static::$_types[$type] : null;
227: }
228:
229: /**
230: * Clears out all created instances and mapped types classes, useful for testing
231: *
232: * @return void
233: */
234: public static function clear()
235: {
236: static::$_types = [];
237: static::$_builtTypes = [];
238: }
239:
240: /**
241: * {@inheritDoc}
242: */
243: public function getName()
244: {
245: return $this->_name;
246: }
247:
248: /**
249: * {@inheritDoc}
250: */
251: public function getBaseType()
252: {
253: return $this->_name;
254: }
255:
256: /**
257: * {@inheritDoc}
258: */
259: public function toDatabase($value, Driver $driver)
260: {
261: return $this->_basicTypeCast($value);
262: }
263:
264: /**
265: * Casts given value from a database type to PHP equivalent
266: *
267: * @param mixed $value Value to be converted to PHP equivalent
268: * @param \Cake\Database\Driver $driver Object from which database preferences and configuration will be extracted
269: * @return mixed
270: */
271: public function toPHP($value, Driver $driver)
272: {
273: return $this->_basicTypeCast($value);
274: }
275:
276: /**
277: * Checks whether this type is a basic one and can be converted using a callback
278: * If it is, returns converted value
279: *
280: * @param mixed $value Value to be converted to PHP equivalent
281: * @return mixed
282: * @deprecated 3.1 All types should now be a specific class
283: */
284: protected function _basicTypeCast($value)
285: {
286: deprecationWarning(
287: 'Using Type::_basicTypeCast() is deprecated. ' .
288: "The '{$this->_name}' type needs to be updated to implement `TypeInterface`."
289: );
290: if ($value === null) {
291: return null;
292: }
293: if (!empty(static::$_basicTypes[$this->_name])) {
294: $typeInfo = static::$_basicTypes[$this->_name];
295: if (isset($typeInfo['callback'])) {
296: return $typeInfo['callback']($value);
297: }
298: }
299:
300: return $value;
301: }
302:
303: /**
304: * {@inheritDoc}
305: */
306: public function toStatement($value, Driver $driver)
307: {
308: if ($value === null) {
309: return PDO::PARAM_NULL;
310: }
311:
312: return PDO::PARAM_STR;
313: }
314:
315: /**
316: * Type converter for boolean values.
317: *
318: * Will convert string true/false into booleans.
319: *
320: * @param mixed $value The value to convert to a boolean.
321: * @return bool
322: * @deprecated 3.1.8 This method is now unused.
323: */
324: public static function boolval($value)
325: {
326: deprecationWarning('Type::boolval() is deprecated.');
327: if (is_string($value) && !is_numeric($value)) {
328: return strtolower($value) === 'true';
329: }
330:
331: return !empty($value);
332: }
333:
334: /**
335: * Type converter for string values.
336: *
337: * Will convert values into strings
338: *
339: * @param mixed $value The value to convert to a string.
340: * @return string
341: * @deprecated 3.1.8 This method is now unused.
342: */
343: public static function strval($value)
344: {
345: deprecationWarning('Type::strval() is deprecated.');
346: if (is_array($value)) {
347: $value = '';
348: }
349:
350: return (string)$value;
351: }
352:
353: /**
354: * {@inheritDoc}
355: */
356: public function newId()
357: {
358: return null;
359: }
360:
361: /**
362: * {@inheritDoc}
363: */
364: public function marshal($value)
365: {
366: return $this->_basicTypeCast($value);
367: }
368:
369: /**
370: * Returns an array that can be used to describe the internal state of this
371: * object.
372: *
373: * @return array
374: */
375: public function __debugInfo()
376: {
377: return [
378: 'name' => $this->_name,
379: ];
380: }
381: }
382: