root/trunk/wwwroot/includes/qcodo/_core/framework/QType.class.php @ 48

Revision 48, 11.8 KB (checked in by alex94040, 6 years ago)

Fixes #25 (Large floats don't validate). Code by VexedPanda?, code review by alex94040.

Line 
1<?php
2        /**
3         * The exception that is thrown by QType::Cast
4         * if an invalid cast is performed.  InvalidCastException
5         * derives from CallerException, and therefore should be handled
6         * similar to how CallerExceptions are handled (e.g. IncrementOffset should
7         * be called whenever an InvalidCastException is caught and rethrown).
8         */
9        class QInvalidCastException extends QCallerException {
10                public function __construct($strMessage, $intOffset = 2) {
11                        parent::__construct($strMessage, $intOffset);
12                }
13        }
14
15        /**
16         * Type Library to add some support for strongly named types.
17         *
18         * PHP does not support strongly named types.  The QCubed type library
19         * and QCubed typing in general attempts to bring some structure to types
20         * when passing in values, properties, parameters to/from QCubed framework objects
21         * and methods.
22         *
23         * The Type library attempts to allow as much flexibility as possible to
24         * set and cast variables to other types, similar to how PHP does it natively,
25         * but simply adds a big more structure to it.
26         *
27         * For example, regardless if a variable is an integer, boolean, or string,
28         * QType::Cast will allow the flexibility of those values to interchange with
29         * each other with little to no issue.
30         *
31         * In addition to value objects (ints, bools, floats, strings), the Type library
32         * also supports object casting.  While technically casting one object to another
33         * is not a true cast, QType::Cast does at least ensure that the tap being "casted"
34         * to is a legitamate subclass of the object being "cast".  So if you have ParentClass,
35         * and you have a ChildClass that extends ParentClass,
36         *              $objChildClass = new ChildClass();
37         *              $objParentClass = new ParentClass();
38         *              Type::Cast($objChildClass, 'ParentClass'); // is a legal cast
39         *              Type::Cast($objParentClass, 'ChildClass'); // will throw an InvalidCastException
40         *
41         * For values, specifically int to string conversion, one different between
42         * QType::Cast and PHP (in order to add structure) is that if an integer contains
43         * alpha characters, PHP would normally allow that through w/o complaint, simply
44         * ignoring any numeric characters past the first alpha character.  QType::Cast
45         * would instead throw an InvalidCastException to let the developer immedaitely
46         * know that something doesn't look right.
47         *
48         * In theory, the type library should maintain the same level of flexibility
49         * PHP developers are accostomed to, while providing a mechanism to limit
50         * careless coding errors and tough to figure out mistakes due to PHP's sometimes
51         * overly laxed type conversions.
52         */
53        abstract class QType {
54                /**
55                 * This faux constructor method throws a caller exception.
56                 * The Type object should never be instantiated, and this constructor
57                 * override simply guarantees it.
58                 *
59                 * @return void
60                 */
61                public final function __construct() {
62                        throw new QCallerException('Type should never be instantiated.  All methods and variables are publically statically accessible.');
63                }
64
65                const String = 'string';
66                const Integer = 'integer';
67                const Float = 'double';
68                const Boolean = 'boolean';
69                const Object = 'object';
70                const ArrayType = 'array';
71
72                const DateTime = 'QDateTime';
73               
74                const Resource = 'resource';
75
76                private static function CastObjectTo($objItem, $strType) {
77                        try {
78                                $objReflection = new ReflectionClass($objItem);
79                                if ($objReflection->getName() == 'SimpleXMLElement') {
80                                        switch ($strType) {
81                                                case QType::String:
82                                                        return (string) $objItem;
83                                                case QType::Integer:
84                                                        try {
85                                                                return QType::Cast((string) $objItem, QType::Integer);
86                                                        } catch (QCallerException $objExc) {
87                                                                $objExc->IncrementOffset();
88                                                                throw $objExc;
89                                                        }
90                                                case QType::Boolean:
91                                                        $strItem = strtolower(trim((string) $objItem));
92                                                        if (($strItem == 'false') ||
93                                                                (!$strItem))
94                                                                return false;
95                                                        else
96                                                                return true;
97                                        }
98                                }
99
100                                if ($objItem instanceof $strType)
101                                        return $objItem;
102                        } catch (Exception $objExc) {
103                        }
104
105                        throw new QInvalidCastException(sprintf('Unable to cast %s object to %s', $objReflection->getName(), $strType));
106                }
107
108                private static function CastValueTo($mixItem, $strType) {
109                        $strItemType = gettype($mixItem);
110
111                        switch (QType::TypeFromDoc($strType)) {
112                                case QType::Boolean:
113                                        if ($strItemType == QType::Boolean)
114                                                return $mixItem;
115                                        if (is_null($mixItem))
116                                                return false;
117                                        if (strlen($mixItem) == 0)
118                                                return false;
119                                        if (strtolower($mixItem) == 'false')
120                                                return false;
121                                        settype($mixItem, $strType);
122                                        return $mixItem;
123
124                                case QType::Integer:
125                                        if (strlen($mixItem) == 0)
126                                                return null;
127                                        if(($mixItem !== true) && ((((string)(int) $mixItem) === ((string) $mixItem))
128                                                                || preg_match('/^-?\d+$/',$mixItem) === 1))
129                                                return $mixItem;
130                                        else
131                                                throw new QInvalidCastException(sprintf('Invalid integer: %s', $mixItem));
132                                case QType::Float:
133                                        if (strlen($mixItem) == 0)
134                                                return null;
135                                        if (!is_numeric($mixItem)) 
136                                                throw new QInvalidCastException(sprintf('Invalid float: %s', $mixItem)); 
137                                        return $mixItem;
138                       
139                                case QType::String:
140                                        $mixOriginal = $mixItem;
141                                        settype($mixItem, $strType);
142
143                                        // Check to make sure the value hasn't changed significantly
144                                        $mixTest = $mixItem;
145                                        settype($mixTest, gettype($mixOriginal));
146
147                                        // Has it?
148                                        if ($mixTest != $mixOriginal)
149                                                // Yes -- therefore this is an invalid cast
150                                                throw new QInvalidCastException(sprintf('Unable to cast %s value to %s: %s', $strItemType, $strType, $mixOriginal));
151
152                                        return $mixItem;
153
154                                default:
155                                        throw new QInvalidCastException(sprintf('Unable to cast %s value to unknown type %s', $strItemType, $strType));
156                        }
157                }
158               
159                private static function CastArrayTo($arrItem, $strType) {
160                        if ($strType == QType::ArrayType)
161                                return $arrItem;
162                        else
163                                throw new QInvalidCastException(sprintf('Unable to cast Array to %s', $strType));
164                }
165
166                /**
167                 * Used to cast a variable to another type.  Allows for moderate
168                 * support of strongly-named types.
169                 *
170                 * Will throw an exception if the cast fails, causes unexpected side effects,
171                 * if attempting to cast an object to a value (or vice versa), or if an object
172                 * is being cast to a class that isn't a subclass (e.g. parent).  The exception
173                 * thrown will be an InvalidCastException, which extends CallerException.
174                 *
175                 * @param mixed $mixItem the value, array or object that you want to cast
176                 * @param string $strType the type to cast to.  Can be a QType::XXX constant (e.g. QType::Integer), or the name of a Class
177                 * @return mixed the passed in value/array/object that has been cast to strType
178                 */
179                public final static function Cast($mixItem, $strType) {
180                        // Automatically Return NULLs
181                        if (is_null($mixItem))
182                                return null;
183
184                        // Figure out what PHP thinks the type is
185                        $strPhpType = gettype($mixItem);
186
187                        switch ($strPhpType) {
188                                case QType::Object:
189                                        try {
190                                                return QType::CastObjectTo($mixItem, $strType);
191                                        } catch (QCallerException $objExc) {
192                                                $objExc->IncrementOffset();
193                                                throw $objExc;
194                                        }
195
196                                case QType::String:
197                                case QType::Integer:
198                                case QType::Float:
199                                case QType::Boolean:
200                                        try {
201                                                return QType::CastValueTo($mixItem, $strType);
202                                        } catch (QCallerException $objExc) {
203                                                $objExc->IncrementOffset();
204                                                throw $objExc;
205                                        }
206
207                                case QType::ArrayType:
208                                        try {
209                                                return QType::CastArrayTo($mixItem, $strType);
210                                        } catch (QCallerException $objExc) {
211                                                $objExc->IncrementOffset();
212                                                throw $objExc;
213                                        }
214
215                                case QType::Resource:
216                                        // Cannot Cast Resources
217                                        throw new QInvalidCastException('Resources cannot be cast');
218
219                                default:
220                                        // Could not determine type
221                                        throw new QInvalidCastException(sprintf('Unable to determine type of item to be cast: %s', $mixItem));
222                        }
223                }
224               
225                /**
226                 * Used by the QCubed Code Generator to allow for the code generation of
227                 * the actual "Type::Xxx" constant, instead of the text of the constant,
228                 * in generated code.
229                 *
230                 * It is rare for Constant to be used manually outside of Code Generation.
231                 *
232                 * @param string $strType the type to convert to 'constant' form
233                 * @return string the text of the Text:Xxx Constant
234                 */
235                public final static function Constant($strType) {
236                        switch ($strType) {
237                                case QType::Object: return 'QType::Object';
238                                case QType::String: return 'QType::String';
239                                case QType::Integer: return 'QType::Integer';
240                                case QType::Float: return 'QType::Float';
241                                case QType::Boolean: return 'QType::Boolean';
242                                case QType::ArrayType: return 'QType::ArrayType';
243                                case QType::Resource: return 'QType::Resource';
244                                case QType::DateTime: return 'QType::DateTime';
245
246                                default:
247                                        // Could not determine type
248                                        throw new QInvalidCastException(sprintf('Unable to determine type of item to lookup its constant: %s', $strType));
249                        }
250                }
251               
252                public final static function TypeFromDoc($strType) {
253                        switch (strtolower($strType)) {
254                                case 'string':
255                                case 'str':
256                                        return QType::String;
257
258                                case 'integer':
259                                case 'int':
260                                        return QType::Integer;
261
262                                case 'float':
263                                case 'flt':
264                                case 'double':
265                                case 'dbl':
266                                case 'single':
267                                case 'decimal':
268                                        return QType::Float;
269
270                                case 'bool':
271                                case 'boolean':
272                                case 'bit':
273                                        return QType::Boolean;
274
275                                case 'datetime':
276                                case 'date':
277                                case 'time':
278                                case 'qdatetime':
279                                        return QType::DateTime;
280
281                                case 'null':
282                                case 'void':
283                                        return 'void';
284
285                                default:
286                                        try {
287                                                $objReflection = new ReflectionClass($strType);
288                                                return $strType;
289                                        } catch (ReflectionException $objExc) {
290                                                throw new QInvalidCastException(sprintf('Unable to determine type of item from PHPDoc Comment to lookup its QType or Class: %s', $strType));
291                                        }
292                        }
293                }
294               
295                /**
296                 * Used by the QCubed Code Generator and QSoapService class to allow for the xml generation of
297                 * the actual "s:type" Soap Variable types.
298                 *
299                 * @param string $strType the type to convert to 'constant' form
300                 * @return string the text of the SOAP standard s:type variable type
301                 */
302                public final static function SoapType($strType) {
303                        switch ($strType) {
304                                case QType::String: return 'string';
305                                case QType::Integer: return 'int';
306                                case QType::Float: return 'float';
307                                case QType::Boolean: return 'boolean';
308                                case QType::DateTime: return 'dateTime';
309
310                                case QType::ArrayType:
311                                case QType::Object:
312                                case QType::Resource:
313                                default:
314                                        // Could not determine type
315                                        throw new QInvalidCastException(sprintf('Unable to determine type of item to lookup its constant: %s', $strType));
316                        }
317                }
318/*
319                final public static function SoapArrayType($strType) {
320                        try {
321                                return sprintf('ArrayOf%s', ucfirst(QType::SoapType($strType)));
322                        } catch (QInvalidCastException $objExc) {}
323                                $objExc->IncrementOffset();
324                                throw $objExc;
325                        }
326                }
327
328                final public static function AlterSoapComplexTypeArray(&$strComplexTypeArray, $strType) {
329                        switch ($strType) {
330                                case QType::String:
331                                        $strItemName = 'string';
332                                        break;
333                                case QType::Integer:
334                                        $strItemName = 'int';
335                                        break;
336                                case QType::Float:
337                                        $strItemName = 'float';
338                                        break;
339                                case QType::Boolean:
340                                        $strItemName = 'boolean';
341                                        break;
342                                case QType::DateTime:
343                                        $strItemName = 'dateTime';
344                                        break;
345
346                                case QType::ArrayType:
347                                case QType::Object:
348                                case QType::Resource:
349                                default:
350                                        // Could not determine type
351                                        throw new QInvalidCastException(sprintf('Unable to determine type of item to lookup its constant: %s', $strType));
352                        }
353
354                        $strArrayName = QType::SoapArrayType($strType);
355
356                        if (!array_key_exists($strArrayName, $strComplexTypeArray))
357                                $strComplexTypeArray[$strArrayName] = sprintf(
358                                        '<s:complexType name="%s"><s:sequence>' .
359                                        '<s:element minOccurs="0" maxOccurs="unbounded" name="%s" type="%s"/>' .
360                                        '</s:sequence></s:complexType>',
361                                        QType::SoapArrayType($strType),
362                                        $strItemName,
363                                        QType::SoapType($strType));
364                }*/
365        }
366?>
Note: See TracBrowser for help on using the browser.