Erebot  latest
A modular IRC bot for PHP 7.0+
Main.php
1 <?php
2 /*
3  This file is part of Erebot, a modular IRC bot written in PHP.
4 
5  Copyright © 2010 François Poirotte
6 
7  Erebot is free software: you can redistribute it and/or modify
8  it under the terms of the GNU General Public License as published by
9  the Free Software Foundation, either version 3 of the License, or
10  (at your option) any later version.
11 
12  Erebot is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with Erebot. If not, see <http://www.gnu.org/licenses/>.
19 */
20 
21 namespace Erebot\Config;
22 
31 class Main extends \Erebot\Config\Proxy implements \Erebot\Interfaces\Config\Main
32 {
34  protected $configFile;
35 
37  protected $networks;
38 
40  protected $version;
41 
43  protected $timezone;
44 
46  protected $commandsPrefix;
47 
49  protected $daemonize;
50 
52  protected $userIdentity;
53 
55  protected $groupIdentity;
56 
58  protected $pidfile;
59 
61  protected $coreTranslator;
62 
86  public function __construct(
87  $configData,
88  $source,
89  \Erebot\Intl\TranslatorInterface $translator
90  ) {
91  $this->proxified = null;
92  $this->modules = array();
93  $this->configFile = null;
94  $this->coreTranslator = $translator;
95  $this->load($configData, $source);
96  }
97 
101  public function __destruct()
102  {
103  parent::__destruct();
104  unset(
105  $this->networks
106  );
107  }
108 
110  public function __clone()
111  {
112  throw new \Exception('Cloning is forbidden');
113  }
114 
125  private function stripXGlobWrappers(&$domxml)
126  {
127  $xpath = new \DOMXPath($domxml);
128  $xpath->registerNamespace('xglob', \Erebot\XGlobStream::XMLNS);
129  $wrappers = $xpath->query('//xglob:' . \Erebot\XGlobStream::TAG);
130  foreach ($wrappers as $wrapper) {
131  for ($i = $wrapper->childNodes->length; $i > 0; $i--) {
132  $wrapper->parentNode->insertBefore(
133  $wrapper->childNodes->item($i - 1),
134  $wrapper->nextSibling
135  );
136  }
137  $wrapper->parentNode->removeChild($wrapper);
138  }
139  }
140 
142  public function load($configData, $source)
143  {
144  $logger = \Plop\Plop::getInstance();
145  $possibleSources = array(
146  self::LOAD_FROM_FILE,
147  self::LOAD_FROM_STRING,
148  );
149  if (!in_array($source, $possibleSources, true)) {
150  throw new \Erebot\InvalidValueException('Invalid $source');
151  }
152 
153  if ($source == self::LOAD_FROM_FILE) {
154  if (is_string($configData) && $configData != "") {
155  if (!strncasecmp(PHP_OS, "Win", 3)) {
156  if (!in_array($configData[0], array("/", "\\")) &&
157  strlen($configData) > 1 && $configData[1] != ":") {
158  $configData = getcwd() . DIRECTORY_SEPARATOR .
159  $configData;
160  }
161  } elseif ($configData[0] != DIRECTORY_SEPARATOR) {
162  $configData = getcwd() . DIRECTORY_SEPARATOR . $configData;
163  }
164  $file = \Erebot\URI::fromAbsPath($configData, false);
165  } elseif (is_object($configData) &&
166  $configData instanceof \Erebot\URIInterface) {
167  $file = $configData;
168  } else {
169  throw new \Erebot\InvalidValueException(
170  "Invalid configuration file"
171  );
172  }
173  } elseif (!is_string($configData)) {
174  throw new \Erebot\InvalidValueException(
175  "Invalid configuration file"
176  );
177  } else {
178  $file = null;
179  }
180 
181  $mainSchema = dirname(dirname(__DIR__)) .
182  DIRECTORY_SEPARATOR . 'data' .
183  DIRECTORY_SEPARATOR . 'config.rng';
184  $mainSchema = file_get_contents($mainSchema);
185  $ue = libxml_use_internal_errors(true);
186  $domxml = new \Erebot\DOM();
187  if ($source == self::LOAD_FROM_FILE) {
188  $domxml->load((string) $file);
189  } else {
190  $domxml->loadXML($configData);
191  }
192 
193  $domxml->xinclude(LIBXML_NOBASEFIX);
194  $this->stripXGlobWrappers($domxml);
195 
196  $ok = $domxml->relaxNGValidateSource($mainSchema);
197  $errors = $domxml->getErrors();
198  libxml_use_internal_errors($ue);
199 
200  if (!$ok || count($errors)) {
201  // Some unpredicted error occurred,
202  // show some (hopefully) useful information.
203  $logger->error(print_r($errors, true));
204  throw new \Erebot\InvalidValueException(
205  'Errors were found while validating the configuration file'
206  );
207  }
208 
209  $xml = simplexml_import_dom($domxml);
210  parent::__construct($this, $xml);
211 
212  if (!isset($xml['version'])) {
213  $this->version = null;
214  } else {
215  $this->version = (string) $xml['version'];
216  }
217 
218  if (!isset($xml['timezone'])) {
219  throw new \Erebot\InvalidValueException('No timezone defined');
220  }
221  $this->timezone = (string) $xml['timezone'];
222 
223  // Set timezone information.
224  // This is needed to configure the logging subsystem.
225  if (function_exists('date_default_timezone_set')) {
226  if (!date_default_timezone_set($this->timezone)) {
227  throw \Erebot\InvalidValueException(
228  sprintf(
229  'Invalid timezone: "%s"',
230  $this->timezone
231  )
232  );
233  }
234  }
235 
236  $daemonize = isset($xml['daemon'])
237  ? $this->parseBool((string) $xml['daemon'])
238  : false;
239  $userIdentity = isset($xml['uid']) ? ((string) $xml['uid']) : null;
240  $groupIdentity = isset($xml['gid']) ? ((string) $xml['gid']) : null;
241  $pidfile = isset($xml['pidfile'])
242  ? ((string) $xml['pidfile'])
243  : null;
244 
245  if ($daemonize === null) {
246  throw new \Erebot\InvalidValueException('Invalid "daemon" value');
247  }
248 
249  if (!isset($xml['commands-prefix'])) {
250  $this->commandsPrefix = '!';
251  } else {
252  $this->commandsPrefix = (string) $xml['commands-prefix'];
253  if (strcspn($this->commandsPrefix, " \r\n\t") !=
254  strlen($this->commandsPrefix)) {
255  throw new \Erebot\InvalidValueException(
256  'Invalid command prefix'
257  );
258  }
259  }
260 
261  $logger->debug(
262  $this->coreTranslator->gettext(
263  'Loaded configuration data'
264  )
265  );
266 
267  $this->networks = array();
268  foreach ($xml->networks->network as $netCfg) {
270  $newConfig = new \Erebot\Config\Network($this, $netCfg);
271  $this->networks[$newConfig->getName()] = $newConfig;
272  unset($newConfig);
273  }
274 
275  if ($source == self::LOAD_FROM_FILE) {
276  $this->configFile = $configData;
277  } else {
278  $this->configFile = null;
279  }
280 
281  // Default values.
282  $this->daemonize = $daemonize;
283  $this->userIdentity = $userIdentity;
284  $this->groupIdentity = $groupIdentity;
285  $this->pidfile = $pidfile;
286  }
287 
289  public function getNetworkCfg($network)
290  {
291  if (!isset($this->networks[$network])) {
292  throw new \Erebot\NotFoundException('No such network');
293  }
294  return $this->networks[$network];
295  }
296 
298  public function getNetworks()
299  {
300  return $this->networks;
301  }
302 
304  public function getVersion()
305  {
306  return $this->version;
307  }
308 
310  public function getTimezone()
311  {
312  return $this->timezone;
313  }
314 
316  public function getCommandsPrefix()
317  {
318  return $this->commandsPrefix;
319  }
320 
322  public function getConfigFile()
323  {
324  return $this->configFile;
325  }
326 
328  public function mustDaemonize()
329  {
330  return $this->daemonize;
331  }
332 
334  public function getGroupIdentity()
335  {
336  return $this->groupIdentity;
337  }
338 
340  public function getUserIdentity()
341  {
342  return $this->userIdentity;
343  }
344 
346  public function getPidfile()
347  {
348  return $this->pidfile;
349  }
350 
352  public function getTranslator($component)
353  {
354  if (isset($this->locale)) {
355  $domain = str_replace('\\', '_', ltrim($component, '\\'));
356  $localedir = static::getBaseDir($component);
357  return \Erebot\Intl\GettextFactory::translation($domain, $localedir, array($this->locale));
358  }
359 
360  return $this->coreTranslator;
361  }
362 }
getTranslator($component)
Definition: Main.php:352
$version
The configuration file's version string.
Definition: Main.php:40
const XMLNS
The XML namespace the content will be wrapped into.
Definition: XGlobStream.php:44
$coreTranslator
Translator used by core files.
Definition: Main.php:61
$configFile
The (relative or absolute) path to the configuration, if available.
Definition: Main.php:34
parseBool($module, $param, $default=null)
Definition: Proxy.php:352
$networks
A list of Erebot::Config::Network objects.
Definition: Main.php:37
$daemonize
Whether to daemonize the bot or not.
Definition: Main.php:49
getNetworkCfg($network)
Definition: Main.php:289
$pidfile
File where the bot's PID will be written.
Definition: Main.php:58
$commandsPrefix
The prefix used to recognize commands.
Definition: Main.php:46
stripXGlobWrappers(&$domxml)
Definition: Main.php:125
Contains the main (general) configuration for Erebot.
Definition: Main.php:31
$timezone
The bot's current timezone.
Definition: Main.php:43
$groupIdentity
Group identity to switch to.
Definition: Main.php:55
const TAG
The XML tag used to wrap the content.
Definition: XGlobStream.php:46
$userIdentity
User identity to switch to.
Definition: Main.php:52
load($configData, $source)
Definition: Main.php:142
__construct($configData, $source,\Erebot\Intl\TranslatorInterface $translator)
Definition: Main.php:86
A configuration proxy which cascades settings.
Definition: Proxy.php:37