| 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 | ?> |
|---|