PageRenderTime 53ms CodeModel.GetById 9ms RepoModel.GetById 0ms app.codeStats 0ms

/branches/0.9/lib/Enumeration/EnumUtils.php

#
PHP | 289 lines | 131 code | 19 blank | 139 comment | 18 complexity | 9a023ba8def1bb6c206b5a3db3495c6e MD5 | raw file
  1. <?php
  2. /**
  3. * @package Curly
  4. * @subpackage Enumeration
  5. * @version 0.9
  6. * @link http://curly.codeplex.com/
  7. * @license http://curly.codeplex.com/license The MIT License
  8. * @author Dawid Zawada
  9. */
  10. namespace Curly\Enumeration
  11. {
  12. /**
  13. * Static utility class for working with enumerations
  14. *
  15. * @package Curly
  16. * @subpackage Enumeration
  17. * @license http://curly.codeplex.com/license The MIT License
  18. */
  19. class EnumUtils
  20. {
  21. /**
  22. * Creates a new enumeration with the name given in the first parameter
  23. * and member names given in the second one
  24. *
  25. * If you want the enumeration to be created in a specific namespace, include the namespace
  26. * in the $className param.
  27. *
  28. * The new enumeration name must obey the rules for class names of the PHP language.
  29. * Member names of the new enumeration must obey the rules for method names of the PHP language.
  30. *
  31. * @example Example of use:
  32. * <code>
  33. * use Enumeration\EnumUtils;
  34. *
  35. * EnumUtils::createEnum( "Fruit", array( "Apple", "Orange" ) );
  36. *
  37. * function isAnApple( Fruit $fruit )
  38. * {
  39. * if( $fruit == Fruit::Apple() )
  40. * {
  41. * return "Yes, it is";
  42. * }
  43. * else
  44. * {
  45. * return "No, it isn't";
  46. * }
  47. * }
  48. *
  49. * echo isAnApple( Friut::Orange() );
  50. * </code>
  51. *
  52. * @param string $className Enumeration name
  53. * @param array $names Array of member names
  54. * @throws \InvalidArgumentException
  55. */
  56. public static function createEnum( $className, array $names )
  57. {
  58. static::validateNewClassName( $className );
  59. $namespace = static::extractNamespace( $className );
  60. $code = "namespace {$namespace} { final class {$className} extends " . __NAMESPACE__ . "\\Enum {";
  61. foreach( $names as $name )
  62. {
  63. static::validateName( $name );
  64. $code .= "public static function {$name}() { return parent::___getInstance( __FUNCTION__ ); }";
  65. }
  66. $code .= "} }";
  67. eval( $code );
  68. }
  69. /**
  70. * Returns names of members of the given enumeration class
  71. *
  72. * The class must exist and must inherit from the Enum class
  73. *
  74. * @param string $className Enumeration name
  75. * @return array
  76. * @throws \InvalidArgumentException
  77. */
  78. public static function getNames( $className )
  79. {
  80. static::validateClassName( $className );
  81. $class = new \ReflectionClass( $className );
  82. $methods = $class->getMethods( \ReflectionMethod::IS_STATIC );
  83. $methods = array_filter( $methods, function( $method ) {
  84. return $method->isPublic();
  85. } );
  86. array_walk( $methods, function( &$method ) {
  87. $method = $method->name;
  88. } );
  89. return array_values( $methods );
  90. }
  91. /**
  92. * Returns an array of members of the given enumeration class
  93. *
  94. * The class must exist and must inherit from the Enum class
  95. *
  96. * @param string $className Enumeration name
  97. * @return array
  98. * @throws \InvalidArgumentException
  99. */
  100. public static function getValues( $className )
  101. {
  102. $methods = static::getNames( $className );
  103. $methods = array_flip( $methods );
  104. array_walk( $methods, function( &$item, $key ) {
  105. $item = $className::$key();
  106. } );
  107. return $methods;
  108. }
  109. /**
  110. * Determines whether the given enumeration class contains a member
  111. * with the specified name
  112. *
  113. * The class must exist and must inherit from the Enum class
  114. *
  115. * @param string $className Enumeration name
  116. * @param string $name Member name
  117. * @return bool
  118. * @throws \InvalidArgumentException
  119. */
  120. public static function isDefined( $className, $name )
  121. {
  122. static::validateClassName( $className );
  123. $class = new \ReflectionClass( $className );
  124. if( $class->hasMethod( $name ) )
  125. {
  126. $method = $class->getMethod( $name );
  127. return $method->isStatic() && $method->isPublic();
  128. }
  129. else
  130. {
  131. return false;
  132. }
  133. }
  134. /**
  135. * Returns the value of the member of the given enumeration class
  136. *
  137. * The class must exist and must inherit from the Enum class.
  138. * The member must exist in the enumeration.
  139. *
  140. * @param string $className Enumeration name
  141. * @param string $name Member name
  142. * @return Curly\Enumeration\Enum
  143. * @throws \InvalidArgumentException
  144. */
  145. public static function parse( $className, $name )
  146. {
  147. if( static::isDefined( $className, $name ) )
  148. {
  149. return $className::$name();
  150. }
  151. else
  152. {
  153. throw new \InvalidArgumentException( "Member '{$name}' not found in the {$className} enumeration" );
  154. }
  155. }
  156. /**
  157. * Returns the namespace part of the given class name
  158. *
  159. * This method also subtracts the namespace part from the variable
  160. * passed by a reference
  161. *
  162. * @param string $className
  163. * @return string
  164. */
  165. protected static function extractNamespace( &$className )
  166. {
  167. $namespace = "";
  168. $lastNsSeparatorPos = strrpos( $className, "\\" );
  169. if( $lastNsSeparatorPos !== false )
  170. {
  171. $namespace = substr( $className, 0, $lastNsSeparatorPos );
  172. $className = substr( $className, $lastNsSeparatorPos + 1 );
  173. if( !empty( $namespace ) && $namespace[0] != "\\" )
  174. {
  175. $namespace = "\\{$namespace}";
  176. }
  177. }
  178. return $namespace;
  179. }
  180. /**
  181. * Throws an exception if the given member name has incorrect format
  182. *
  183. * Throws an exception if:
  184. * the name is empty or
  185. * the name does not obey the rules for method names of the PHP language or
  186. * the name starts with more than one underscore character
  187. *
  188. * @internal
  189. * @param string $name Member name
  190. * @throws \InvalidArgumentException
  191. */
  192. public static function _validateName( $name )
  193. {
  194. if( empty( $name ) )
  195. {
  196. throw new \InvalidArgumentException( "Name of an enumeration member cannot be empty" );
  197. }
  198. if( !preg_match( "~^[a-zA-Z_\\x7f-\\xff][a-zA-Z0-9_\\x7f-\\xff]*$~", $name ) )
  199. {
  200. // the name doesn't obey the rules for method names of the PHP language
  201. throw new \InvalidArgumentException( "Name of an enumeration member contains illegal characters. The name must obey the rules for method names of the PHP language" );
  202. }
  203. if( strpos( $name, "__" ) === 0 )
  204. {
  205. // the name starts with more than one underscore character
  206. throw new \InvalidArgumentException( "Name of an enumeration member cannot start with more than one underscore character" );
  207. }
  208. }
  209. /**
  210. * Throws an exception if there is no enumeration with the given name
  211. *
  212. * Throws an exception if:
  213. * the name is empty or
  214. * a class with the specified name doesn't exist or
  215. * the class doesn't inherit from the Enum class
  216. *
  217. * @param string $className Enumeration name
  218. * @throws \InvalidArgumentException
  219. */
  220. protected static function validateClassName( $className )
  221. {
  222. if( empty( $className ) )
  223. {
  224. throw new \InvalidArgumentException( "Class name cannot be empty" );
  225. }
  226. if( !class_exists( $className ) )
  227. {
  228. throw new \InvalidArgumentException( "Class '{$className}' does not exist" );
  229. }
  230. if( !is_subclass_of( $className, __NAMESPACE__ . "\\Enum" ) )
  231. {
  232. throw new \InvalidArgumentException( "Class '{$className}' must be a child of the '" . __NAMESPACE__ . "\\Enum' class" );
  233. }
  234. }
  235. /**
  236. * Throws an exception if there already is an enumeration with the given name
  237. *
  238. * Throws an exception if:
  239. * the name is empty or
  240. * the name does not obey the rules for class names of the PHP language or
  241. * a class with the specified name already exists
  242. *
  243. * @param string $className Enumeration name
  244. * @throws \InvalidArgumentException
  245. */
  246. protected static function validateNewClassName( $className )
  247. {
  248. if( empty( $className ) )
  249. {
  250. throw new \InvalidArgumentException( "Class name cannot be empty" );
  251. }
  252. if( !preg_match( "~^[a-zA-Z_\\x7f-\\xff][a-zA-Z0-9_\\x7f-\\xff]*$~", $className ) )
  253. {
  254. // the name doesn't obey the rules for class names of the PHP language
  255. throw new \InvalidArgumentException( "Class name contains illegal characters. The name must obey the rules for class names of the PHP language" );
  256. }
  257. if( class_exists( $className ) )
  258. {
  259. throw new \InvalidArgumentException( "Class '{$className}' already exists" );
  260. }
  261. }
  262. /**
  263. * We don't want this class and its subclasses to be instantiated
  264. */
  265. private final function __construct()
  266. {
  267. throw new \LogicException( "'EnumUtils' class cannot be instantiated" );
  268. }
  269. }
  270. }