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