Core  3.2
PHP API documentation
 All Data Structures Namespaces Files Functions Variables Pages
classAutoloader.php
Go to the documentation of this file.
1 <?php
2 /*
3  * @author Gérald Croes
4  * @author Anakeen
5  * @package FDL
6 */
7 
8 namespace Dcp;
9 
11 {
12  private static function getAutoloader()
13  {
14  return DirectoriesAutoloader::instance(DEFAULT_PUBDIR, '.autoloader.cache');
15  }
16  private static function configure(DirectoriesAutoloader $autoloader)
17  {
18  return $autoloader->addDirectory('./')->addCustomFilter('\Dcp\autoloaderIgnoreDotD');
19  }
20  /**
21  * Register Dynacase Platform autoloader.
22  */
23  public static function register()
24  {
25  require_once 'WHAT/classAutoloader.php';
26  require_once 'WHAT/classAutoloaderIgnoreDotD.php';
27  include_once 'WHAT/Lib.Prefix.php';
28 
29  self::configure(self::getAutoloader())->register();
30  }
31  /**
32  * Check if a class is known by the autoloader.
33  * @param string $pClassName The class's name
34  * @return bool true if the class is known by the autoloader, otherwise false.
35  */
36  public static function classExists($pClassName)
37  {
38  return self::getAutoloader()->classExists($pClassName);
39  }
40  /**
41  *
42  */
43  public static function forceRegenerate()
44  {
45  require_once 'WHAT/Lib.Prefix.php';
46  require_once 'WHAT/classAutoloaderIgnoreDotD.php';
47 
48  $autoloader = self::configure(self::getAutoloader());
49  $cacheFile = $autoloader->getCacheFilePath();
50  if (is_file($cacheFile)) {
51  unlink($cacheFile);
52  }
53  $autoloader->forceRegenerate('');
54  }
55 }
56 
57 class ExtensionFilterIteratorDecorator extends \FilterIterator
58 {
59  private $_ext;
60  /**
61  * Check whether the current element of the iterator is acceptable
62  *
63  * @see FilterIterator::accept()
64  *
65  * @return bool true if the current element is acceptable, otherwise false.
66  *
67  */
68  public function accept()
69  {
70  if (substr($this->current() , -1 * strlen($this->_ext)) === $this->_ext) {
71  return is_readable($this->current());
72  }
73  return false;
74  }
75  /**
76  * define which extension is allowed
77  *
78  * @param string $pExt extension
79  *
80  * @return void
81  */
82  public function setExtension($pExt)
83  {
84  $this->_ext = $pExt;
85  }
86 }
87 /**
88  * Interface for classes in charge of detecting classes declarations in files
89  *
90  * @author dev@dynacase.org <dev@dynacase.org>
91  *
92  */
93 interface IClassHunter {
94  /**
95  * Find all classes in given file
96  *
97  * @param string $pFileName file name
98  *
99  * @return array of strings
100  *
101  */
102  public function find($pFileName);
103 }
104 /**
105  * Class in charge of detecting classes declarations in files (only for PHP 5.3)
106  *
107  * @author Gérald Croes
108  * @author Anakeen
109  *
110  */
112 {
113  /**
114  * Find all classes in given file
115  *
116  * @param string $pFileName file name
117  *
118  * @return array of strings
119  *
120  */
121  public function find($pFileName)
122  {
123  if (!defined("T_TRAIT")) define("T_TRAIT", "T_TRAIT"); // defined in PHP 5.4 only
124  $toReturn = array();
125  $tokens = token_get_all(file_get_contents($pFileName, false));
126  $currentNamespace = '';
127  $namespaceHunt = false;
128  $validatedNamespaceHunt = false;
129  $classHunt = false;
130  $whitespaceCount = 0;
131  foreach ($tokens as $token) {
132  if (is_array($token)) {
133  if ($token[0] === T_INTERFACE || $token[0] === T_CLASS || $token[0] === T_TRAIT) {
134  $classHunt = true;
135  continue;
136  } elseif ($token[0] === T_NAMESPACE) {
137  $namespaceHunt = true;
138  continue;
139  }
140  if ($classHunt && $token[0] === T_STRING) {
141  $toReturn[(strlen($currentNamespace) > 0 ? $currentNamespace . '\\' : '') . $token[1]] = $pFileName;
142  $classHunt = false;
143  } elseif ($namespaceHunt && $validatedNamespaceHunt && ($token[0] === T_STRING || $token[0] === T_NS_SEPARATOR)) {
144  $currentNamespace.= $token[1];
145  } elseif ($namespaceHunt && !$validatedNamespaceHunt && $token[0] === T_WHITESPACE) {
146  $currentNamespace = '';
147  $validatedNamespaceHunt = true;
148  } elseif ($namespaceHunt && !$validatedNamespaceHunt && $token[0] !== T_WHITESPACE) {
149  $namespaceHunt = false;
150  }
151  } else {
152  if ($token === ';' || $token === '{') {
153  //can validate namespace only when using namespace{} syntax
154  if ($namespaceHunt && !$validatedNamespaceHunt && $token === '{') {
155  $currentNamespace = '';
156  }
157  $classHunt = false;
158  $namespaceHunt = false;
159  $validatedNamespaceHunt = false;
160  }
161  }
162  }
163  return $toReturn;
164  }
165 }
166 
168 {
169 }
171 {
172  /**
173  * @var \dcp\ClassHunterForPHP5_3
174  */
175  private $_classHunterStrategy;
176  private $_lockfd = false;
177  //--- Singleton
178 
179  /**
180  * make constructor private
181  *
182  * @return \dcp\DirectoriesAutoloader
183  */
184  private function __construct()
185  {
186  }
187  /**
188  *
189  * @var DirectoriesAutoloader
190  */
191  private static $_instance = false;
192  /**
193  * get singleton for DirectoriesAutoloader
194  *
195  * @param string $pTmpPath path to tmp dir
196  * @param string $pTmpFileName tmp file name
197  *
198  * @return \dcp\DirectoriesAutoloader
199  */
200  public static function instance($pTmpPath, $pTmpFileName = 'directoriesautoloader.cache.php')
201  {
202  if (self::$_instance === false) {
203  self::$_instance = new DirectoriesAutoloader();
204  self::$_instance->setCachePath($pTmpPath);
205  self::$_instance->setCacheFileName($pTmpFileName);
206  self::$_instance->_classHunterStrategy = ClassHunterFactory::create(PHP_VERSION);
207  }
208  return self::$_instance;
209  }
210  //--- /Singleton
211 
212  /**
213  * register autoloader against SPL
214  *
215  * @return boolean
216  */
217  public function register()
218  {
219  return spl_autoload_register(array(
220  $this,
221  'autoload'
222  ));
223  }
224  /**
225  * force autoloader to regenerate cache now!
226  *
227  * @return \dcp\DirectoriesAutoloader
228  */
229  public function forceRegenerate($className)
230  {
231  $this->_canRegenerate = true;
232  $this->_classes = array();
233  $this->_regenerate($className);
234  $this->_saveIncache();
235 
236  return self::$_instance;
237  }
238  /*
239  * Enable dryrun/test mode
240  */
241  private $_dryRun = false;
242  public function dryRun($dryRun = true)
243  {
244  $this->_dryRun = $dryRun;
245  return self::$_instance;
246  }
247  //--- Cache
248  private $_cachePath;
249  private $_cacheFileName = 'directoriesautoloader.cache.php';
250  /**
251  * define the path of tmp directory
252  *
253  * @param string $pTmp cache directory path
254  *
255  * @throws \dcp\DirectoriesAutoloaderException
256  *
257  * @return void
258  */
259  public function setCachePath($pTmp)
260  {
261  if (!is_writable($pTmp)) {
262  throw new DirectoriesAutoloaderException('Cannot write in given CachePath [' . $pTmp . ']');
263  }
264  $this->_cachePath = $pTmp;
265  }
266  /**
267  * define the name of tmp file
268  *
269  * @param string $ptmpFileName tmp file name
270  *
271  * @return void
272  */
273  public function setCacheFileName($ptmpFileName)
274  {
275  $this->_cacheFileName = $ptmpFileName;
276  }
277  /**
278  * get the fully qualified tmp file path
279  *
280  * @return string
281  */
282  public function getCacheFilePath()
283  {
284  return $this->_cachePath . '/' . $this->_cacheFileName;
285  }
286  /**
287  * get the cache lock file
288  *
289  * @return string
290  */
291  private function _getCacheLockFilePath()
292  {
293  return $this->getCacheFilePath() . '.lock';
294  }
295  //--- /Cache
296  //--- custom filters
297  private $_customFilterClasses = null;
298  /**
299  * add an instance of FilterIterator as custom filter
300  *
301  * @param string $pCustomFilterClass className of customFilter to add
302  *
303  * @throws \dcp\DirectoriesAutoloaderException
304  *
305  * @return \dcp\DirectoriesAutoloader
306  */
307  public function addCustomFilter($pCustomFilterClass)
308  {
309  $filterClassParents = class_parents($pCustomFilterClass);
310  if (isset($filterClassParents['FilterIterator'])) {
311  if (!is_array($this->_customFilterClasses)) {
312  $this->_customFilterClasses = array(
313  $pCustomFilterClass
314  );
315  //error_log("adding [$pCustomFilterClass] as custom filter");
316 
317  } else {
318  $this->_customFilterClasses[] = $pCustomFilterClass;
319  }
320  } else {
321  throw new DirectoriesAutoloaderException('Custom filter class [' . $pCustomFilterClass . '] does not inherits from FilterIterator');
322  }
323  return self::$_instance;
324  }
325  //--- /custom filters
326  //--- Autoload
327 
328  /**
329  * @throws DirectoriesAutoloaderException
330  */
331  private function _lock()
332  {
333  if ($this->_dryRun) {
334  return;
335  }
336  if ($this->_lockfd !== false) {
337  throw new DirectoriesAutoloaderException(sprintf("Cache lock is already opened."));
338  }
339  $lockfile = $this->_getCacheLockFilePath();
340  $this->_lockfd = fopen($lockfile, 'a');
341  if ($this->_lockfd === false) {
342  throw new DirectoriesAutoloaderException(sprintf("Error opening cache lock file '%s'.", $lockfile));
343  }
344  if (flock($this->_lockfd, LOCK_EX) === false) {
345  throw new DirectoriesAutoloaderException(sprintf("Error locking cache lock file '%s'.", $lockfile));
346  }
347  }
348  /**
349  * @throws DirectoriesAutoloaderException
350  */
351  private function _unlock()
352  {
353  if ($this->_dryRun) {
354  return;
355  }
356  if ($this->_lockfd === false) {
357  throw new DirectoriesAutoloaderException(sprintf("Cache lock not opened."));
358  }
359  if (flock($this->_lockfd, LOCK_UN) === false) {
360  fclose($this->_lockfd);
361  $this->_lockfd = false;
362  throw new DirectoriesAutoloaderException(sprintf("Error unlocking cache lock."));
363  }
364  fclose($this->_lockfd);
365  $this->_lockfd = false;
366  }
367  /**
368  * @param $pClassName
369  * @return bool
370  * @throws \Exception
371  */
372  private function _regenerate($pClassName)
373  {
374  $this->_lock();
375  /*
376  * Re-check the cache in case of a concurrent process
377  * which might have already re-built the cache.
378  */
379  $this->_classes = array(); // Empty the cache to force a re-validation from the cache file
380  if ($this->_loadClass($pClassName)) {
381  $this->_unlock();
382  return true;
383  }
384  /*
385  * If the re-validation failed, then the cache is now re-populated with the content of the cache file,
386  * so we re-empty it before regenerating the new content
387  */
388  $this->_classes = array();
389  /*
390  * Re-build the cache
391  */
392  try {
393  $this->_includesAll();
394  $this->addFamilies('FDLGEN');
395  $this->_saveInCache();
396  }
397  catch(\Exception $e) {
398  $this->_unlock();
399  throw $e;
400  }
401  $this->_unlock();
402  return false;
403  }
404  /**
405  * autoloader
406  *
407  * @param string $pClassName name of the class to load
408  *
409  * @return boolean
410  */
411  public function autoload($pClassName)
412  {
413  //do we already know this class?
414  if ($this->_loadClass($pClassName)) {
415  return true;
416  }
417  //If we are allowed to regenerate autoload file, we try again
418  if ($this->_canRegenerate) {
419  $this->_regenerate($pClassName);
420  $this->_canRegenerate = false; //avoid loops
421  return $this->autoload($pClassName);
422  }
423  //we really found nothing
424  return false;
425  }
426  private $_canRegenerate = true;
427  //--- /Autoload
428 
429  /**
430  * look for all classes into registered directories
431  *
432  * @return void
433  */
434  private function _includesAll()
435  {
436  $cwd = getcwd();
437  $err = "";
438  //include known classes
439  foreach ($this->_directories as $directory => $recursive) {
440  /*
441  * Relative directories are handled relative to _cachePath
442  */
443  $changedCwd = false;
444  if (strpos($directory, '/') !== 0) {
445  $ret = chdir($this->_cachePath . DIRECTORY_SEPARATOR . $directory);
446  if ($ret === false) {
447  continue;
448  }
449  $changedCwd = true;
450  }
451 
452  $directories = new \AppendIterator();
453  //add all paths that we want to browse
454  if ($recursive) {
455  $directories->append(new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($directory) , \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD));
456  } else {
457  $directories->append(new \DirectoryIterator($directory));
458  }
459  if (is_array($this->_customFilterClasses)) {
460  foreach ($this->_customFilterClasses as $customFilterClass) {
461  //error_log("trying to use [$customFilterClass] as filter");
462  //error_log("directories was a " . get_class($directories));
463  $directories = new $customFilterClass($directories);
464  //error_log("directories is now a " . get_class($directories));
465 
466  }
467  }
468  //restrict files to php ones
469  $files = new ExtensionFilterIteratorDecorator($directories);
470  $files->setExtension('.php');
471 
472  foreach ($files as $fileName) {
473  $classes = $this->_classHunterStrategy->find((string)$fileName);
474  foreach ($classes as $className => $fileName2) {
475  $fileName1 = (isset($this->_classes[strtolower($className) ])) ? $this->_classes[strtolower($className) ] : null;
476  if ($fileName1 !== null && realpath($fileName1) !== false && realpath($fileName2) !== realpath($fileName1)) {
477  $err.= ($err ? "\n" : '') . sprintf(_("Class %s from file %s already declared in autoload with file %s") , $className, $fileName2, $this->_classes[strtolower($className) ]);
478  } else {
479  $this->_classes[strtolower($className) ] = $fileName2;
480  }
481  }
482  }
483  if ($changedCwd) {
484  chdir($cwd);
485  }
486  }
487  if ($err) {
488  require_once "WHAT/Class.Log.php";
489  $log = new \Log();
490  $log->setLogLevel("WEI");
491  $log->error($err);
492  throw new DirectoriesAutoloaderException($err);
493  }
494  //error_log('included all classes as ' . var_export($this->_classes, true));
495 
496  }
497  /**
498  * array of files containing classes indexed by class name
499  *
500  * @var array $_classes
501  */
502  private $_classes = array();
503  /**
504  * get file path where className is defined
505  * @param string $className
506  * @return string|null
507  */
508  public function getClassFile($className)
509  {
510  return isset($this->_classes[strtolower($className) ]) ? $this->_classes[strtolower($className) ] : null;
511  }
512  /**
513  * write cache file
514  *
515  * @see DirectoriesAutoloader::_classes
516  * @see DirectoriesAutoloader::getCacheFilePath
517  *
518  * @throws \dcp\DirectoriesAutoloaderException
519  *
520  * @return void
521  */
522  private function _saveIncache()
523  {
524  if ($this->_dryRun) {
525  return;
526  }
527  foreach ($this->_classes as $className => & $fileName) {
528  if (substr($fileName, 0, 2) == './') {
529  $fileName = substr($fileName, 2);
530  }
531  }
532  unset($fileName);
533 
534  $toSave = '<?php' . PHP_EOL;
535  $toSave.= '// Cache generated at: ' . date(DATE_W3C) . PHP_EOL;
536  $toSave.= '$classes = ' . var_export($this->_classes, true);
537  $toSave.= '; ?>';
538  /*
539  * Atomically write .autoloader.cache file
540  */
541  $cacheFileName = $this->getCacheFilePath();
542  $cacheDirName = dirname($cacheFileName);
543  $tmpFile = tempnam($cacheDirName, '.autoloader.cache.tmp');
544  if ($tmpFile === false) {
545  throw new DirectoriesAutoloaderException("Error creating temporary autoloader cache file.");
546  }
547  if (file_put_contents($tmpFile, $toSave) === false) {
548  unlink($tmpFile);
549  throw new DirectoriesAutoloaderException(sprintf("Error writing cache content to temporary file '%s'", $tmpFile));
550  }
551  if (rename($tmpFile, $cacheFileName) === false) {
552  unlink($tmpFile);
553  throw new DirectoriesAutoloaderException(sprintf("Error renaming temporary cache file '%s' to '%s'.", $tmpFile, $cacheFileName));
554  }
555  chmod($cacheFileName, (0666 ^ umask()));
556  }
557  /**
558  * try to load a class
559  *
560  * @param string $pClassName class name
561  *
562  * @return boolean
563  */
564  private function _loadClass($pClassName)
565  {
566  $className = strtolower($pClassName);
567  if ($this->classExists($className)) {
568  include_once ('WHAT/Lib.Prefix.php');
569  if (!file_exists(DEFAULT_PUBDIR . DIRECTORY_SEPARATOR . $this->_classes[$className])) {
570  return false;
571  }
572  require_once $this->_classes[$className];
573  if (!class_exists($className, false) && !interface_exists($className, false)) {
574  if (function_exists("trait_exists") && trait_exists($className, false)) {
575  return true;
576  }
577  return false;
578  }
579  return true;
580  }
581  return false;
582  }
583  /**
584  * Check if a class exists in the autoloader's cache
585  *
586  * @param $pClassName
587  * @return bool
588  */
589  public function classExists($pClassName)
590  {
591  $className = ltrim(strtolower($pClassName),"\\");
592  if (count($this->_classes) === 0) {
593  if (is_readable($this->getCacheFilePath())) {
594  $classes = array();
595  require $this->getCacheFilePath(); // load $classes here
596  $this->_classes = $classes;
597  }
598  }
599  return isset($this->_classes[$className]);
600  }
601  /**
602  * add a directory to autoloaded directories
603  *
604  * @param string $pDirectory directory path
605  * @param boolean $pRecursive should we recursively scan this directory
606  *
607  * @throws \dcp\DirectoriesAutoloaderException
608  *
609  * @return \dcp\DirectoriesAutoloader
610  */
611  public function addDirectory($pDirectory, $pRecursive = true)
612  {
613  $absoluteDirectory = $pDirectory;
614  if (strpos($pDirectory, '/') !== 0) {
615  $absoluteDirectory = $this->_cachePath . DIRECTORY_SEPARATOR . $pDirectory;
616  }
617  if (!is_readable($absoluteDirectory)) {
618  throw new DirectoriesAutoloaderException('Cannot read from [' . $absoluteDirectory . ']');
619  }
620  $this->_directories[$pDirectory] = $pRecursive ? true : false;
621  return self::$_instance;
622  }
623  /**
624  * add Family classes in cache
625  * @param string $genDirectory generate family directory
626  * @return bool|DirectoriesAutoloader
627  * @throws DirectoriesAutoloaderException
628  */
629  public function addFamilies($genDirectory)
630  {
631  include_once ("Lib.Common.php");
632  $sql = "select * from pg_tables where tablename = 'docfam'";
633  $err = \simpleQuery('', $sql, $exists);
634  if (count($exists) > 0) {
635  $sql = 'select id, "name" from docfam where name is not null order by id';
636  $err = \simpleQuery('', $sql, $famNames);
637  if ($err) {
638  throw new DirectoriesAutoloaderException('Cannot access family name [' . $err . ']');
639  }
640  foreach ($famNames as $aFam) {
641  $aFamName = $aFam["name"];
642  $aFamId = $aFam["id"];
643  $this->_classes['_' . strtolower($aFamName) ] = sprintf("%s/Class.Doc%d.php", $genDirectory, $aFamId);
644  $this->_classes['dcp\\family\\' . strtolower($aFamName) ] = sprintf("%s/Class.Doc%d.php", $genDirectory, $aFamId);
645  $this->_classes['dcp\\attributeidentifiers\\' . strtolower($aFamName) ] = sprintf("%s/Class.Attrid%d.php", $genDirectory, $aFamId);
646  }
647  }
648  return self::$_instance;
649  }
650  private $_directories = array();
651 }
653 {
654  /**
655  * Factory to decide which classHunter we use (should we use namespaces or not?)
656  *
657  * @param string $version php version
658  *
659  * @return \dcp\ClassHunterForPHP5_3
660  */
661  public static function create($version)
662  {
663  return new ClassHunterForPHP5_3();
664  }
665 }
addCustomFilter($pCustomFilterClass)
Exception class use exceptionCode to identifiy correctly exception.
Definition: exceptions.php:19
$ret
static instance($pTmpPath, $pTmpFileName= 'directoriesautoloader.cache.php')
const DEFAULT_PUBDIR
Definition: Lib.Prefix.php:28
$log
Definition: wsh.php:33
static classExists($pClassName)
$className
if($_REQUEST['submitted']) else $cwd
Definition: test.php:66
addDirectory($pDirectory, $pRecursive=true)
static forceRegenerate()
simpleQuery($dbaccess, $query, &$result=array(), $singlecolumn=false, $singleresult=false, $useStrict=null)
Definition: Lib.Common.php:484
static create($version)
if($file) if($subject==""&&$file) if($subject=="") $err
← centre documentaire © anakeen