PageRenderTime 48ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 1ms

/core/ClassInfo.php

http://github.com/silverstripe/sapphire
PHP | 291 lines | 134 code | 42 blank | 115 comment | 29 complexity | 10e1760ed5c9e80743815ed73d919b24 MD5 | raw file
Possible License(s): BSD-3-Clause, MIT, CC-BY-3.0, GPL-2.0, AGPL-1.0, LGPL-2.1
  1. <?php
  2. use SilverStripe\ORM\DB;
  3. use SilverStripe\ORM\DataObject;
  4. /**
  5. * Provides introspection information about the class tree.
  6. *
  7. * It's a cached wrapper around the built-in class functions. SilverStripe uses
  8. * class introspection heavily and without the caching it creates an unfortunate
  9. * performance hit.
  10. *
  11. * @package framework
  12. * @subpackage core
  13. */
  14. class ClassInfo {
  15. /**
  16. * Wrapper for classes getter.
  17. *
  18. * @return array
  19. */
  20. public static function allClasses() {
  21. return SS_ClassLoader::instance()->getManifest()->getClasses();
  22. }
  23. /**
  24. * Returns true if a class or interface name exists.
  25. *
  26. * @param string $class
  27. * @return bool
  28. */
  29. public static function exists($class) {
  30. return class_exists($class, false) || interface_exists($class, false) || SS_ClassLoader::instance()->getItemPath($class);
  31. }
  32. /**
  33. * Cache for {@link hasTable()}
  34. */
  35. private static $_cache_all_tables = array();
  36. /**
  37. * @var Array Cache for {@link ancestry()}.
  38. */
  39. private static $_cache_ancestry = array();
  40. /**
  41. * @todo Move this to SS_Database or DB
  42. */
  43. public static function hasTable($class) {
  44. // Cache the list of all table names to reduce on DB traffic
  45. if(empty(self::$_cache_all_tables) && DB::is_active()) {
  46. self::$_cache_all_tables = DB::get_schema()->tableList();
  47. }
  48. return !empty(self::$_cache_all_tables[strtolower($class)]);
  49. }
  50. public static function reset_db_cache() {
  51. self::$_cache_all_tables = null;
  52. self::$_cache_ancestry = array();
  53. }
  54. /**
  55. * Returns the manifest of all classes which are present in the database.
  56. *
  57. * @param string $class Class name to check enum values for ClassName field
  58. * @param boolean $includeUnbacked Flag indicating whether or not to include
  59. * types that don't exist as implemented classes. By default these are excluded.
  60. * @return array List of subclasses
  61. */
  62. public static function getValidSubClasses($class = 'SilverStripe\\CMS\\Model\\SiteTree', $includeUnbacked = false) {
  63. if(is_string($class) && !class_exists($class)) return array();
  64. $class = self::class_name($class);
  65. if ($includeUnbacked) {
  66. $table = DataObject::getSchema()->tableName($class);
  67. $classes = DB::get_schema()->enumValuesForField($table, 'ClassName');
  68. } else {
  69. $classes = static::subclassesFor($class);
  70. }
  71. return $classes;
  72. }
  73. /**
  74. * Returns an array of the current class and all its ancestors and children
  75. * which require a DB table.
  76. *
  77. * @todo Move this into {@see DataObjectSchema}
  78. *
  79. * @param string|object $class
  80. * @return array
  81. */
  82. public static function dataClassesFor($class) {
  83. if(is_string($class) && !class_exists($class)) return array();
  84. $result = array();
  85. $class = self::class_name($class);
  86. $classes = array_merge(
  87. self::ancestry($class),
  88. self::subclassesFor($class)
  89. );
  90. foreach ($classes as $class) {
  91. if (DataObject::has_own_table($class)) {
  92. $result[$class] = $class;
  93. }
  94. }
  95. return $result;
  96. }
  97. /**
  98. * @deprecated 4.0..5.0
  99. */
  100. public static function baseDataClass($class) {
  101. Deprecation::notice('5.0', 'Use DataObject::getSchema()->baseDataClass()');
  102. return DataObject::getSchema()->baseDataClass($class);
  103. }
  104. /**
  105. * Returns a list of classes that inherit from the given class.
  106. * The resulting array includes the base class passed
  107. * through the $class parameter as the first array value.
  108. *
  109. * Example usage:
  110. * <code>
  111. * ClassInfo::subclassesFor('BaseClass');
  112. * array(
  113. * 'BaseClass' => 'BaseClass',
  114. * 'ChildClass' => 'ChildClass',
  115. * 'GrandChildClass' => 'GrandChildClass'
  116. * )
  117. * </code>
  118. *
  119. * @param mixed $class string of the classname or instance of the class
  120. * @return array Names of all subclasses as an associative array.
  121. */
  122. public static function subclassesFor($class) {
  123. if(is_string($class) && !class_exists($class)) {
  124. return [];
  125. }
  126. //normalise class case
  127. $className = self::class_name($class);
  128. $descendants = SS_ClassLoader::instance()->getManifest()->getDescendantsOf($class);
  129. $result = array($className => $className);
  130. if ($descendants) {
  131. return $result + ArrayLib::valuekey($descendants);
  132. } else {
  133. return $result;
  134. }
  135. }
  136. /**
  137. * Convert a class name in any case and return it as it was defined in PHP
  138. *
  139. * eg: self::class_name('dataobJEct'); //returns 'DataObject'
  140. *
  141. * @param string|object $nameOrObject The classname or object you want to normalise
  142. * @return string The normalised class name
  143. */
  144. public static function class_name($nameOrObject) {
  145. if (is_object($nameOrObject)) {
  146. return get_class($nameOrObject);
  147. }
  148. $reflection = new ReflectionClass($nameOrObject);
  149. return $reflection->getName();
  150. }
  151. /**
  152. * Returns the passed class name along with all its parent class names in an
  153. * array, sorted with the root class first.
  154. *
  155. * @param string $class
  156. * @param bool $tablesOnly Only return classes that have a table in the db.
  157. * @return array
  158. */
  159. public static function ancestry($class, $tablesOnly = false) {
  160. if(is_string($class) && !class_exists($class)) return array();
  161. $class = self::class_name($class);
  162. $lClass = strtolower($class);
  163. $cacheKey = $lClass . '_' . (string)$tablesOnly;
  164. $parent = $class;
  165. if(!isset(self::$_cache_ancestry[$cacheKey])) {
  166. $ancestry = array();
  167. do {
  168. if (!$tablesOnly || DataObject::has_own_table($parent)) {
  169. $ancestry[$parent] = $parent;
  170. }
  171. } while ($parent = get_parent_class($parent));
  172. self::$_cache_ancestry[$cacheKey] = array_reverse($ancestry);
  173. }
  174. return self::$_cache_ancestry[$cacheKey];
  175. }
  176. /**
  177. * @return array A self-keyed array of class names. Note that this is only available with Silverstripe
  178. * classes and not built-in PHP classes.
  179. */
  180. public static function implementorsOf($interfaceName) {
  181. return SS_ClassLoader::instance()->getManifest()->getImplementorsOf($interfaceName);
  182. }
  183. /**
  184. * Returns true if the given class implements the given interface
  185. */
  186. public static function classImplements($className, $interfaceName) {
  187. return in_array($className, self::implementorsOf($interfaceName));
  188. }
  189. /**
  190. * Get all classes contained in a file.
  191. * @uses ManifestBuilder
  192. *
  193. * @todo Doesn't return additional classes that only begin
  194. * with the filename, and have additional naming separated through underscores.
  195. *
  196. * @param string $filePath Path to a PHP file (absolute or relative to webroot)
  197. * @return array
  198. */
  199. public static function classes_for_file($filePath) {
  200. $absFilePath = Director::getAbsFile($filePath);
  201. $matchedClasses = array();
  202. $manifest = SS_ClassLoader::instance()->getManifest()->getClasses();
  203. foreach($manifest as $class => $compareFilePath) {
  204. if($absFilePath == $compareFilePath) $matchedClasses[] = $class;
  205. }
  206. return $matchedClasses;
  207. }
  208. /**
  209. * Returns all classes contained in a certain folder.
  210. *
  211. * @todo Doesn't return additional classes that only begin
  212. * with the filename, and have additional naming separated through underscores.
  213. *
  214. * @param string $folderPath Relative or absolute folder path
  215. * @return array Array of class names
  216. */
  217. public static function classes_for_folder($folderPath) {
  218. $absFolderPath = Director::getAbsFile($folderPath);
  219. $matchedClasses = array();
  220. $manifest = SS_ClassLoader::instance()->getManifest()->getClasses();
  221. foreach($manifest as $class => $compareFilePath) {
  222. if(stripos($compareFilePath, $absFolderPath) === 0) $matchedClasses[] = $class;
  223. }
  224. return $matchedClasses;
  225. }
  226. private static $method_from_cache = array();
  227. public static function has_method_from($class, $method, $compclass) {
  228. $lClass = strtolower($class);
  229. $lMethod = strtolower($method);
  230. $lCompclass = strtolower($compclass);
  231. if (!isset(self::$method_from_cache[$lClass])) self::$method_from_cache[$lClass] = array();
  232. if (!array_key_exists($lMethod, self::$method_from_cache[$lClass])) {
  233. self::$method_from_cache[$lClass][$lMethod] = false;
  234. $classRef = new ReflectionClass($class);
  235. if ($classRef->hasMethod($method)) {
  236. $methodRef = $classRef->getMethod($method);
  237. self::$method_from_cache[$lClass][$lMethod] = $methodRef->getDeclaringClass()->getName();
  238. }
  239. }
  240. return strtolower(self::$method_from_cache[$lClass][$lMethod]) == $lCompclass;
  241. }
  242. /**
  243. * @deprecated 4.0..5.0
  244. */
  245. public static function table_for_object_field($candidateClass, $fieldName) {
  246. Deprecation::notice('5.0', 'Use DataObject::getSchema()->tableForField()');
  247. return DataObject::getSchema()->tableForField($candidateClass, $fieldName);
  248. }
  249. }