1: <?php
2:
3: 4: 5: 6:
7:
8: namespace Nette\DI;
9:
10: use Nette;
11:
12:
13: 14: 15: 16: 17:
18: class ContainerFactory extends Nette\Object
19: {
20:
21: public $onCompile;
22:
23:
24: public $autoRebuild = FALSE;
25:
26:
27: public $class = 'SystemContainer';
28:
29:
30: public $parentClass = 'Nette\DI\Container';
31:
32:
33: public $config = array();
34:
35:
36: public $configFiles = array();
37:
38:
39: public $tempDirectory;
40:
41:
42: private $dependencies = array();
43:
44:
45: public function __construct($tempDirectory)
46: {
47: $this->tempDirectory = $tempDirectory;
48: }
49:
50:
51: 52: 53:
54: public function create()
55: {
56: if (!class_exists($this->class)) {
57: $this->loadClass();
58: }
59: return new $this->class;
60: }
61:
62:
63: 64: 65:
66: protected function generateCode()
67: {
68: $compiler = $this->createCompiler();
69: $config = $this->generateConfig();
70: $this->onCompile($this, $compiler, $config);
71:
72: $code = "<?php\n";
73: foreach ($this->configFiles as $info) {
74: if (is_scalar($info[0])) {
75: $code .= "// source: $info[0] $info[1]\n";
76: }
77: }
78: $code .= "\n" . $compiler->compile($config, $this->class, $this->parentClass);
79:
80: if ($this->autoRebuild !== 'compat') {
81: $this->dependencies = array_merge($this->dependencies, $compiler->getContainerBuilder()->getDependencies());
82: }
83: return $code;
84: }
85:
86:
87: 88: 89:
90: protected function generateConfig()
91: {
92: $config = array();
93: $loader = $this->createLoader();
94: foreach ($this->configFiles as $info) {
95: $info = is_scalar($info[0]) ? $loader->load($info[0], $info[1]) : $info[0];
96: $config = Config\Helpers::merge($info, $config);
97: }
98: $this->dependencies = array_merge($this->dependencies, $loader->getDependencies());
99:
100: return Config\Helpers::merge($config, $this->config);
101: }
102:
103:
104: 105: 106:
107: private function loadClass()
108: {
109: $key = md5(serialize(array($this->config, $this->configFiles, $this->class, $this->parentClass)));
110: $file = "$this->tempDirectory/$key.php";
111: if (!$this->isExpired($file) && (@include $file) !== FALSE) {
112: return;
113: }
114:
115: $handle = fopen("$file.lock", 'c+');
116: if (!$handle || !flock($handle, LOCK_EX)) {
117: throw new Nette\IOException("Unable to acquire exclusive lock on '$file.lock'.");
118: }
119:
120: if (!is_file($file) || $this->isExpired($file)) {
121: $this->dependencies = array();
122: $toWrite[$file] = $this->generateCode();
123: $files = $this->dependencies ? array_combine($this->dependencies, $this->dependencies) : array();
124: $toWrite["$file.meta"] = serialize(@array_map('filemtime', $files));
125:
126: foreach ($toWrite as $name => $content) {
127: if (file_put_contents("$name.tmp", $content) !== strlen($content) || !rename("$name.tmp", $name)) {
128: @unlink("$name.tmp");
129: throw new Nette\IOException("Unable to create file '$name'.");
130: }
131: }
132: }
133:
134: if ((@include $file) === FALSE) {
135: throw new Nette\IOException("Unable to include '$file'.");
136: }
137: flock($handle, LOCK_UN);
138: }
139:
140:
141: private function isExpired($file)
142: {
143: if ($this->autoRebuild) {
144: $meta = @unserialize(file_get_contents("$file.meta"));
145: $files = $meta ? array_combine($tmp = array_keys($meta), $tmp) : array();
146: return $meta !== @array_map('filemtime', $files);
147: }
148: return FALSE;
149: }
150:
151:
152: 153: 154:
155: protected function createCompiler()
156: {
157: return new Compiler;
158: }
159:
160:
161: 162: 163:
164: protected function createLoader()
165: {
166: return new Config\Loader;
167: }
168:
169: }
170: