Merge pull request #18725 from civicrm/5.31
[civicrm-core.git] / Civi / Api4 / Utils / ReflectionUtils.php
CommitLineData
19b53e5b
C
1<?php
2/*
3 +--------------------------------------------------------------------+
41498ac5 4 | Copyright CiviCRM LLC. All rights reserved. |
19b53e5b 5 | |
41498ac5
TO
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
19b53e5b
C
9 +--------------------------------------------------------------------+
10 */
11
12namespace Civi\Api4\Utils;
13
14/**
15 * Just another place to put static functions...
16 */
17class ReflectionUtils {
18
19 /**
20 * @param \Reflector|\ReflectionClass $reflection
21 * @param string $type
22 * If we are not reflecting the class itself, specify "Method", "Property", etc.
fc95d9a5
CW
23 * @param array $vars
24 * Variable substitutions to perform in the docblock
19b53e5b
C
25 * @return array
26 */
fc95d9a5
CW
27 public static function getCodeDocs($reflection, $type = NULL, $vars = []) {
28 $comment = $reflection->getDocComment();
e3c6d5ff
CW
29 foreach ($vars as $key => $val) {
30 $comment = str_replace('$' . strtoupper(\CRM_Utils_String::pluralize($key)), \CRM_Utils_String::pluralize($val), $comment);
31 $comment = str_replace('$' . strtoupper($key), $val, $comment);
fc95d9a5
CW
32 }
33 $docs = self::parseDocBlock($comment);
19b53e5b
C
34
35 // Recurse into parent functions
36 if (isset($docs['inheritDoc']) || isset($docs['inheritdoc'])) {
37 unset($docs['inheritDoc'], $docs['inheritdoc']);
38 $newReflection = NULL;
39 try {
40 if ($type) {
41 $name = $reflection->getName();
42 $reflectionClass = $reflection->getDeclaringClass()->getParentClass();
43 if ($reflectionClass) {
44 $getItem = "get$type";
45 $newReflection = $reflectionClass->$getItem($name);
46 }
47 }
48 else {
49 $newReflection = $reflection->getParentClass();
50 }
51 }
52 catch (\ReflectionException $e) {
53 }
54 if ($newReflection) {
55 // Mix in
fc95d9a5 56 $additionalDocs = self::getCodeDocs($newReflection, $type, $vars);
19b53e5b
C
57 if (!empty($docs['comment']) && !empty($additionalDocs['comment'])) {
58 $docs['comment'] .= "\n\n" . $additionalDocs['comment'];
59 }
60 $docs += $additionalDocs;
61 }
62 }
63 return $docs;
64 }
65
66 /**
67 * @param string $comment
68 * @return array
69 */
70 public static function parseDocBlock($comment) {
71 $info = [];
9cea3619 72 $param = NULL;
19b53e5b
C
73 foreach (preg_split("/((\r?\n)|(\r\n?))/", $comment) as $num => $line) {
74 if (!$num || strpos($line, '*/') !== FALSE) {
75 continue;
76 }
136ca5bb
CW
77 $line = ltrim(trim($line), '*');
78 if (strlen($line) && $line[0] === ' ') {
79 $line = substr($line, 1);
80 }
81 if (strpos(ltrim($line), '@') === 0) {
82 $words = explode(' ', ltrim($line, ' @'));
83 $key = array_shift($words);
9cea3619 84 $param = NULL;
19b53e5b 85 if ($key == 'var') {
9cea3619 86 $info['type'] = explode('|', $words[0]);
19b53e5b 87 }
0493ec47
CW
88 elseif ($key == 'return') {
89 $info['return'] = explode('|', $words[0]);
90 }
19b53e5b 91 elseif ($key == 'options') {
9cea3619 92 $val = str_replace(', ', ',', implode(' ', $words));
19b53e5b
C
93 $info['options'] = explode(',', $val);
94 }
0493ec47 95 elseif ($key == 'throws' || $key == 'see') {
9cea3619
CW
96 $info[$key][] = implode(' ', $words);
97 }
98 elseif ($key == 'param' && $words) {
99 $type = $words[0][0] !== '$' ? explode('|', array_shift($words)) : NULL;
100 $param = rtrim(array_shift($words), '-:()/');
101 $info['params'][$param] = [
102 'type' => $type,
103 'description' => $words ? ltrim(implode(' ', $words), '-: ') : '',
104 'comment' => '',
105 ];
106 }
19b53e5b
C
107 else {
108 // Unrecognized annotation, but we'll duly add it to the info array
9cea3619 109 $val = implode(' ', $words);
19b53e5b
C
110 $info[$key] = strlen($val) ? $val : TRUE;
111 }
112 }
9cea3619
CW
113 elseif ($param) {
114 $info['params'][$param]['comment'] .= $line . "\n";
115 }
19b53e5b 116 elseif ($num == 1) {
fc95d9a5 117 $info['description'] = ucfirst($line);
19b53e5b
C
118 }
119 elseif (!$line) {
120 if (isset($info['comment'])) {
121 $info['comment'] .= "\n";
122 }
fc95d9a5
CW
123 else {
124 $info['comment'] = NULL;
125 }
126 }
127 // For multi-line description.
128 elseif (count($info) === 1 && isset($info['description']) && substr($info['description'], -1) !== '.') {
129 $info['description'] .= ' ' . $line;
19b53e5b
C
130 }
131 else {
132 $info['comment'] = isset($info['comment']) ? "{$info['comment']}\n$line" : $line;
133 }
134 }
135 if (isset($info['comment'])) {
fc95d9a5 136 $info['comment'] = rtrim($info['comment']);
19b53e5b
C
137 }
138 return $info;
139 }
140
141 /**
142 * List all traits used by a class and its parents.
143 *
144 * @param object|string $class
145 * @return array
146 */
147 public static function getTraits($class) {
148 $traits = [];
149 // Get traits of this class + parent classes
150 do {
151 $traits = array_merge(class_uses($class), $traits);
152 } while ($class = get_parent_class($class));
153 // Get traits of traits
154 foreach ($traits as $trait => $same) {
155 $traits = array_merge(class_uses($trait), $traits);
156 }
157 return $traits;
158 }
159
160}