PageRenderTime 43ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/includes/qcodo/_core/framework/QType.class.php

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