1: <?php
2:
3: 4: 5: 6:
7:
8: namespace Nette\DI;
9:
10: use Nette;
11:
12:
13: 14: 15:
16: class ContainerLoader
17: {
18: use Nette\SmartObject;
19:
20:
21: private $autoRebuild = false;
22:
23:
24: private $tempDirectory;
25:
26:
27: public function __construct($tempDirectory, $autoRebuild = false)
28: {
29: $this->tempDirectory = $tempDirectory;
30: $this->autoRebuild = $autoRebuild;
31: }
32:
33:
34: 35: 36: 37: 38:
39: public function load($generator, $key = null)
40: {
41: if (!is_callable($generator)) {
42: trigger_error(__METHOD__ . ': order of arguments has been swapped.', E_USER_DEPRECATED);
43: list($generator, $key) = [$key, $generator];
44: }
45: $class = $this->getClassName($key);
46: if (!class_exists($class, false)) {
47: $this->loadFile($class, $generator);
48: }
49: return $class;
50: }
51:
52:
53: 54: 55:
56: public function getClassName($key)
57: {
58: return 'Container_' . substr(md5(serialize($key)), 0, 10);
59: }
60:
61:
62: 63: 64:
65: private function loadFile($class, $generator)
66: {
67: $file = "$this->tempDirectory/$class.php";
68: if (!$this->isExpired($file) && (@include $file) !== false) {
69: return;
70: }
71:
72: Nette\Utils\FileSystem::createDir($this->tempDirectory);
73:
74: $handle = @fopen("$file.lock", 'c+');
75: if (!$handle) {
76: throw new Nette\IOException("Unable to create file '$file.lock'. " . error_get_last()['message']);
77: } elseif (!@flock($handle, LOCK_EX)) {
78: throw new Nette\IOException("Unable to acquire exclusive lock on '$file.lock'. " . error_get_last()['message']);
79: }
80:
81: if (!is_file($file) || $this->isExpired($file, $updatedMeta)) {
82: if (isset($updatedMeta)) {
83: $toWrite["$file.meta"] = $updatedMeta;
84: } else {
85: list($toWrite[$file], $toWrite["$file.meta"]) = $this->generate($class, $generator);
86: }
87:
88: foreach ($toWrite as $name => $content) {
89: if (file_put_contents("$name.tmp", $content) !== strlen($content) || !rename("$name.tmp", $name)) {
90: @unlink("$name.tmp");
91: throw new Nette\IOException("Unable to create file '$name'.");
92: } elseif (function_exists('opcache_invalidate')) {
93: @opcache_invalidate($name, true);
94: }
95: }
96: }
97:
98: if ((@include $file) === false) {
99: throw new Nette\IOException("Unable to include '$file'.");
100: }
101: flock($handle, LOCK_UN);
102: }
103:
104:
105: private function isExpired($file, &$updatedMeta = null)
106: {
107: if ($this->autoRebuild) {
108: $meta = @unserialize((string) file_get_contents("$file.meta"));
109: $orig = $meta[2];
110: return empty($meta[0])
111: || DependencyChecker::isExpired(...$meta)
112: || ($orig !== $meta[2] && $updatedMeta = serialize($meta));
113: }
114: return false;
115: }
116:
117:
118: 119: 120:
121: protected function generate($class, $generator)
122: {
123: $compiler = new Compiler;
124: $compiler->setClassName($class);
125: $code = call_user_func_array($generator, [&$compiler]) ?: $compiler->compile();
126: return [
127: "<?php\n$code",
128: serialize($compiler->exportDependencies()),
129: ];
130: }
131: }
132: