1: <?php
2:
3: 4: 5: 6:
7:
8: namespace Nette\DI;
9:
10: use Nette;
11: use ReflectionClass;
12: use ReflectionMethod;
13: use Nette\Utils\Reflection;
14:
15:
16: 17: 18:
19: class DependencyChecker
20: {
21: const VERSION = 1;
22:
23: use Nette\SmartObject;
24:
25:
26: private $dependencies = [];
27:
28:
29: 30: 31: 32:
33: public function add(array $deps)
34: {
35: $this->dependencies = array_merge($this->dependencies, $deps);
36: return $this;
37: }
38:
39:
40: 41: 42: 43:
44: public function export()
45: {
46: $files = $phpFiles = $classes = $functions = [];
47: foreach ($this->dependencies as $dep) {
48: if (is_string($dep)) {
49: $files[] = $dep;
50:
51: } elseif ($dep instanceof ReflectionClass) {
52: if (empty($classes[$name = $dep->getName()])) {
53: $all = [$name] + class_parents($name) + class_implements($name);
54: foreach ($all as & $item) {
55: $all += class_uses($item);
56: $phpFiles[] = (new ReflectionClass($item))->getFileName();
57: $classes[$item] = TRUE;
58: }
59: }
60:
61: } elseif ($dep instanceof \ReflectionFunctionAbstract) {
62: $phpFiles[] = $dep->getFileName();
63: $functions[] = Reflection::toString($dep);
64:
65: } else {
66: throw new Nette\InvalidStateException('Unexpected dependency ' . gettype($dep));
67: }
68: }
69:
70: $classes = array_keys($classes);
71: $functions = array_unique($functions, SORT_REGULAR);
72: $hash = self::calculateHash($classes, $functions);
73: $files = @array_map('filemtime', array_combine($files, $files));
74: $phpFiles = @array_map('filemtime', array_combine($phpFiles, $phpFiles));
75: return [self::VERSION, $files, $phpFiles, $classes, $functions, $hash];
76: }
77:
78:
79: 80: 81: 82:
83: public static function isExpired($version, $files, $phpFiles, $classes, $functions, $hash)
84: {
85: $current = @array_map('filemtime', array_combine($tmp = array_keys($files), $tmp));
86: $currentClass = @array_map('filemtime', array_combine($tmp = array_keys($phpFiles), $tmp));
87: return $version !== self::VERSION
88: || $files !== $current
89: || ($phpFiles !== $currentClass && $hash !== self::calculateHash($classes, $functions));
90: }
91:
92:
93: private static function calculateHash($classes, $functions)
94: {
95: $hash = [];
96: foreach ($classes as $name) {
97: try {
98: $class = new ReflectionClass($name);
99: } catch (\ReflectionException $e) {
100: return;
101: }
102: $hash[] = [$name, Reflection::getUseStatements($class), $class->isAbstract()];
103: foreach ($class->getProperties(\ReflectionProperty::IS_PUBLIC) as $prop) {
104: if ($prop->getDeclaringClass() == $class) {
105: $hash[] = [$name, $prop->getName(), $prop->getDocComment()];
106: }
107: }
108: foreach ($class->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
109: if ($method->getDeclaringClass() == $class) {
110: $hash[] = [
111: $name,
112: $method->getName(),
113: $method->getDocComment(),
114: self::hashParameters($method),
115: PHP_VERSION_ID >= 70000 && $method->hasReturnType()
116: ? [(string) $method->getReturnType(), $method->getReturnType()->allowsNull()]
117: : NULL
118: ];
119: }
120: }
121: }
122:
123: $flip = array_flip($classes);
124: foreach ($functions as $name) {
125: try {
126: $method = strpos($name, '::') ? new ReflectionMethod($name) : new \ReflectionFunction($name);
127: } catch (\ReflectionException $e) {
128: return;
129: }
130: $class = $method instanceof ReflectionMethod ? $method->getDeclaringClass() : NULL;
131: if ($class && isset($flip[$class->getName()])) {
132: continue;
133: }
134: $hash[] = [
135: $name,
136: $class ? Reflection::getUseStatements($method->getDeclaringClass()) : NULL,
137: $method->getDocComment(),
138: self::hashParameters($method),
139: PHP_VERSION_ID >= 70000 && $method->hasReturnType()
140: ? [(string) $method->getReturnType(), $method->getReturnType()->allowsNull()]
141: : NULL
142: ];
143: }
144:
145: return md5(serialize($hash));
146: }
147:
148:
149: private static function hashParameters(\ReflectionFunctionAbstract $method)
150: {
151: $res = [];
152: if (PHP_VERSION_ID < 70000 && $method->getNumberOfParameters() && $method->getFileName()) {
153: $res[] = file($method->getFileName())[$method->getStartLine() - 1];
154: }
155: foreach ($method->getParameters() as $param) {
156: $res[] = [
157: $param->getName(),
158: PHP_VERSION_ID >= 70000 ? [Reflection::getParameterType($param), $param->allowsNull()] : NULL,
159: $param->isVariadic(),
160: $param->isDefaultValueAvailable()
161: ? [Reflection::getParameterDefaultValue($param)]
162: : NULL
163: ];
164: }
165: return $res;
166: }
167:
168: }
169: