Erebot  latest
A modular IRC bot for PHP 5.3+
Autoload.php
1 <?php
2 
3 namespace Erebot;
4 
15 class Autoload
16 {
18  protected static $registered = false;
19 
21  protected static $paths = array();
22 
24  protected static $map = array();
25 
27  protected static $maps = array();
28 
30  protected static $mapfile = null;
31 
33  protected static $unmapped = array();
34 
47  public static function initialize($path = null, $mapfile = null)
48  {
49  self::register();
50  self::addPath($path);
51  self::addMap($mapfile);
52  }
53 
60  protected static function register()
61  {
62  if (!self::$registered) {
63  // set up __autoload
64  $autoload = spl_autoload_functions();
65  spl_autoload_register(__CLASS__.'::load');
66  if (function_exists('__autoload') && ($autoload === false)) {
67  // __autoload() was being used, but now would be ignored, add
68  // it to the autoload stack
69  spl_autoload_register('__autoload');
70  }
71  }
72  self::$registered = true;
73  }
74 
84  protected static function addPath($path)
85  {
86  $len = strlen(DIRECTORY_SEPARATOR);
87  while (1) {
88  if (!is_string($path)) {
89  return;
90  }
91 
92  if (substr($path, -$len) == DIRECTORY_SEPARATOR) {
93  $path = substr($path, 0, -$len);
94  } else {
95  break;
96  }
97  }
98 
99  if (!in_array($path, self::$paths)) {
100  self::$paths[] = $path;
101  }
102  }
103 
113  protected static function addMap($mapfile)
114  {
115  if (! in_array($mapfile, self::$maps)) {
116  // keep track of specific map file loaded in this
117  // instance so we can update it if necessary
118  self::$mapfile = $mapfile;
119 
120  if (file_exists($mapfile)) {
121  $map = include $mapfile;
122  if (is_array($map)) {
123  // mapfile contains a valid map, so we'll keep it
124  self::$maps[] = $mapfile;
125  self::$map = array_merge(self::$map, $map);
126  }
127  }
128  }
129  }
130 
141  protected static function isMapped($class)
142  {
143  if (isset(self::$map[$class])) {
144  return true;
145  }
146  if (isset(self::$mapfile) && ! isset(self::$map[$class])) {
147  self::$unmapped[] = $class;
148  return false;
149  }
150  return false;
151  }
152 
163  public static function load($class)
164  {
165  /* Protects us from possible remote code inclusion due to:
166  https://bugs.php.net/bug.php?id=55475
167  We should already be safe without this check due to the
168  way this autoloader works, but this helps other autoloaders
169  that use other mechanisms and may be vulnerable. */
170  if (strpos($class, ":") !== false) {
171  // Safer than returning false as it prevents
172  // other autoloaders from ever executing...
173  throw new \Exception('Possible remote code injection detected');
174  }
175 
176  // need to check if there's a current map file specified ALSO.
177  // this could be the first time writing it.
178  $mapped = self::isMapped($class);
179  if ($mapped) {
180  require_once self::$map[$class];
181  if (!self::loadSuccessful($class)) {
182  // record this failure & keep going, we may still find it
183  self::$unmapped[] = $class;
184  } else {
185  return true;
186  }
187  }
188 
189  $file = str_replace(
190  array('_', '\\'),
191  DIRECTORY_SEPARATOR,
192  $class
193  ) . '.php';
194  foreach (self::$paths as $path) {
195  if (file_exists($path . DIRECTORY_SEPARATOR . $file)) {
196  require_once $path . DIRECTORY_SEPARATOR . $file;
197  if (!self::loadSuccessful($class)) {
198  throw new \Exception(
199  'Class ' . $class . ' was not present in ' .
200  $path . DIRECTORY_SEPARATOR . $file .
201  '") [Autoload]'
202  );
203  }
204 
205  if (in_array($class, self::$unmapped)) {
206  self::updateMap(
207  $class,
208  $path . DIRECTORY_SEPARATOR . $file
209  );
210  }
211  return true;
212  }
213  }
214 
215  $e = new \Exception(
216  'Class ' . $class . ' could not be loaded from ' .
217  $file . ', file does not exist (registered paths="' .
218  implode(PATH_SEPARATOR, self::$paths) .
219  '") [Autoload]'
220  );
221  $trace = $e->getTrace();
222  if (isset($trace[2]) && isset($trace[2]['function']) &&
223  in_array(
224  $trace[2]['function'],
225  array('class_exists', 'interface_exists')
226  )) {
227  return false;
228  }
229  if (isset($trace[1]) && isset($trace[1]['function']) &&
230  in_array(
231  $trace[1]['function'],
232  array('class_exists', 'interface_exists')
233  )) {
234  return false;
235  }
236 
237  // If there are other autoload functions registered,
238  // let's try to play nicely with them...
239  // ...otherwise, we just throw an exception.
240  if (count(spl_autoload_functions()) == 1) {
241  throw $e;
242  }
243  return false;
244  }
245 
256  protected static function loadSuccessful($class)
257  {
258  if (!class_exists($class, false) &&
259  !interface_exists($class, false)) {
260  return false;
261  }
262  return true;
263  }
264 
278  protected static function updateMap($class, $origin)
279  {
280  if (is_writable(self::$mapfile) ||
281  is_writable(dirname(self::$mapfile))) {
282  self::$map[$class] = $origin;
283  file_put_contents(
284  self::$mapfile,
285  '<'."?php\n"
286  . "// Autoload auto-generated classmap\n"
287  . "return " . var_export(self::$map, true) . ';',
288  LOCK_EX
289  );
290  }
291  }
292 
299  public static function getPaths()
300  {
301  return self::$paths;
302  }
303 }
Definition: CLI.php:21
static getPaths()
Definition: Autoload.php:299
static addPath($path)
Definition: Autoload.php:84
Autoloader for Erebot&#39;s classes and interfaces.
Definition: Autoload.php:15
static load($class)
Definition: Autoload.php:163
static addMap($mapfile)
Definition: Autoload.php:113
static initialize($path=null, $mapfile=null)
Definition: Autoload.php:47
static isMapped($class)
Definition: Autoload.php:141
static loadSuccessful($class)
Definition: Autoload.php:256
static updateMap($class, $origin)
Definition: Autoload.php:278