Add already existing codebase
This commit is contained in:
commit
80c62f33ce
14 changed files with 941 additions and 0 deletions
69
class/core/Path.class.php
Normal file
69
class/core/Path.class.php
Normal file
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
|
||||
namespace core;
|
||||
|
||||
/**
|
||||
* Generic class to specify string type
|
||||
*/
|
||||
class Path
|
||||
{
|
||||
/**
|
||||
* Path value
|
||||
*
|
||||
* @var \string
|
||||
*/
|
||||
public \array $parts;
|
||||
|
||||
/**
|
||||
* Constructor of Path
|
||||
*
|
||||
* @param \string $value Path string
|
||||
*/
|
||||
public function __construct(\string $value)
|
||||
{
|
||||
$parts = \mb_split(\DIRECTORY_SEPARATOR, $value);
|
||||
if ($parts === False)
|
||||
{
|
||||
\throw \core\PathException(_('cannot extract path components for the given value'), 1);
|
||||
}
|
||||
$this->parts = $parts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a child path to this parent path without caring that the child is inside the parent
|
||||
*
|
||||
* @param \core\Path | \string $path Child path
|
||||
*/
|
||||
public function append(path: \core\Path | \string: $path) : self
|
||||
{
|
||||
if (\is_string($path))
|
||||
{
|
||||
$path = \core\Path($path);
|
||||
}
|
||||
$this->parts = \array_merge($this->parts, $path->parts);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a child path to this parent path, checking if the result path is inside the parent path as it should
|
||||
*
|
||||
* @param \core\Path | \string $path Child path
|
||||
*/
|
||||
public function safe_append(path: \core\Path | \string $path) : self
|
||||
{
|
||||
$old_realpath = \realpath($this->__tostring());
|
||||
$new_realpath = \realpath($this->append($path)->__tostring());
|
||||
if (\strpos($new_realpath, $old_realpath) !== 0)
|
||||
{
|
||||
$this = \core\Path($old_realpath);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function __toString() : \string
|
||||
{
|
||||
return \DIRECTORY_SEPARATOR . \implode(\DIRECTORY_SEPARATOR, $this->parts);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
78
class/core/config/Configuration.class.php
Normal file
78
class/core/config/Configuration.class.php
Normal file
|
@ -0,0 +1,78 @@
|
|||
<?php
|
||||
|
||||
namespace core\config;
|
||||
|
||||
/**
|
||||
* Configuration
|
||||
*/
|
||||
class Configuration
|
||||
{
|
||||
/**
|
||||
* Configuration constructor
|
||||
*
|
||||
* @param \array[\string] $configuration Array containing configuration value string
|
||||
*/
|
||||
public function __construct(configuration: \array[\string] $configuration)
|
||||
{
|
||||
$this->configuration = $configuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a subpart of this configuration or a value in this configuration
|
||||
*
|
||||
* @param \string $keys,... Ordered tuple of key, which give the address of the wanted part
|
||||
*
|
||||
* @return \core\config\Configuration | \string
|
||||
*/
|
||||
public function get(keys: \string ...$keys) : \core\config\Configuration | \string
|
||||
{
|
||||
$part = $this->configuration;
|
||||
|
||||
foreach ($keys as $key)
|
||||
{
|
||||
if (\key_exists($key, $part))
|
||||
{
|
||||
\throw new \core\config\ConfigException(
|
||||
\core\substitute(_('the key {key} does not exist in this part of the configuration'), array('key' => $key))
|
||||
);
|
||||
}
|
||||
$part = $part[$key];
|
||||
}
|
||||
if (\is_array($part))
|
||||
{
|
||||
return new \core\config\Configuration($part);
|
||||
}
|
||||
if (\is_string($part))
|
||||
{
|
||||
return $part
|
||||
}
|
||||
\throw new \core\config\ConfigException(
|
||||
\core\substitute(_('the type {type} should not be in a configuration'), array('type' => \gettype($part)))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a subpart of this configuration or a value in this configuration
|
||||
*
|
||||
* @param \string $keys,... Ordered tuple of key, which give the address of the wanted part
|
||||
*
|
||||
* @return \bool
|
||||
*/
|
||||
public function exist(keys: \string ...$keys) : \bool
|
||||
{
|
||||
$part = $this->configuration;
|
||||
|
||||
foreach ($keys as $key)
|
||||
{
|
||||
if (\key_exists($key, $part))
|
||||
{
|
||||
return False;
|
||||
}
|
||||
$part = $part[$key];
|
||||
}
|
||||
|
||||
return True;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
68
class/core/config/GlobalConfiguration.class.php
Normal file
68
class/core/config/GlobalConfiguration.class.php
Normal file
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
|
||||
namespace core\config;
|
||||
|
||||
/**
|
||||
* Special configuration children, used especialy for the whole configuration
|
||||
*/
|
||||
class GlobalConfiguration
|
||||
{
|
||||
/**
|
||||
* Equivalent to get method of \core\config\Configuration, but load the whole configuration
|
||||
*
|
||||
* @param \string $keys,... Ordered tuple of keys
|
||||
*
|
||||
* @return \core\config\Configuration | \string
|
||||
*/
|
||||
public static function read(...$keys) : \core\config\Configuration | \string
|
||||
{
|
||||
$part = self::load();
|
||||
|
||||
foreach ($keys as $key)
|
||||
{
|
||||
if (\key_exists($key, $part))
|
||||
{
|
||||
\throw new \core\config\ConfigException(
|
||||
\core\substitute(_('the key {key} does not exist in this part of the configuration'), array('key' => $key))
|
||||
);
|
||||
}
|
||||
$part = $part[$key];
|
||||
}
|
||||
if (\is_array($part))
|
||||
{
|
||||
return new \core\config\Configuration($part);
|
||||
}
|
||||
if (\is_string($part))
|
||||
{
|
||||
return $part
|
||||
}
|
||||
\throw new \core\config\ConfigException(
|
||||
\core\substitute(_('the type {type} should not be in a configuration'), array('type' => \gettype($part)))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a subpart of this configuration or a value in this configuration
|
||||
*
|
||||
* @param \string $keys,... Ordered tuple of key, which give the address of the wanted part
|
||||
*
|
||||
* @return \bool
|
||||
*/
|
||||
public function exist(keys: \string ...$keys) : \bool
|
||||
{
|
||||
$part = self::load();
|
||||
|
||||
foreach ($keys as $key)
|
||||
{
|
||||
if (\key_exists($key, $part))
|
||||
{
|
||||
return False;
|
||||
}
|
||||
$part = $part[$key];
|
||||
}
|
||||
|
||||
return True;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
49
class/core/exception/CustomException.class.php
Normal file
49
class/core/exception/CustomException.class.php
Normal file
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
namespace core\exception;
|
||||
|
||||
abstract class CustomException extends \Exception
|
||||
{
|
||||
/**
|
||||
* default tags associated to this exception
|
||||
*
|
||||
* @var \class[]
|
||||
*/
|
||||
const TAGS = [];
|
||||
|
||||
/**
|
||||
* CustomException constructor
|
||||
*
|
||||
* @param ?\string $message Error message (default to null)
|
||||
*
|
||||
* @param \int $code User defined code (default to 0)
|
||||
*
|
||||
* @param ?\Throwable $previous throwable (default to null)
|
||||
*
|
||||
* @param ?\array $tags Tags of the generated log event (default to null)
|
||||
*/
|
||||
public function __construct(?\string $message = null, \int $code = 0, ?\Throwable $previous = null, ?\array $tags = null)
|
||||
{
|
||||
if (\is_null($message))
|
||||
{
|
||||
$message = _("empty message")
|
||||
}
|
||||
if (\is_null($tags))
|
||||
{
|
||||
$tags = $this::TAGS;
|
||||
}
|
||||
|
||||
parent::__construct($message, $code, $previous);
|
||||
}
|
||||
|
||||
public funtion __tostring() : string
|
||||
{
|
||||
return \htmlspecialchars(\get_class($this)) .
|
||||
' ' . \htmlspecialchars($this->message) .
|
||||
' in ' . \htmlspecialchars($this->file) .
|
||||
'(' . \htmlspecialchars($this->line) . ')\n' .
|
||||
\htmlspecialchars($this->getTraceAsString());
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
85
class/core/log/Event.class.php
Normal file
85
class/core/log/Event.class.php
Normal file
|
@ -0,0 +1,85 @@
|
|||
<?php
|
||||
|
||||
namespace core\log;
|
||||
|
||||
/**
|
||||
* A log event
|
||||
*/
|
||||
class Event
|
||||
{
|
||||
/**
|
||||
* Message associated to this event
|
||||
*
|
||||
* @var \string
|
||||
*/
|
||||
protected \string $message;
|
||||
|
||||
/**
|
||||
* Time associated to this event
|
||||
*
|
||||
* @var \int
|
||||
*/
|
||||
protected \int $timestamp;
|
||||
|
||||
/**
|
||||
* Tags associated to this event
|
||||
*
|
||||
* @var \core\log\Tag[]
|
||||
*/
|
||||
protected \array $tags;
|
||||
|
||||
/**
|
||||
* Backtrace associated to this event
|
||||
*
|
||||
* @var ?\array
|
||||
*/
|
||||
protected ?\array $backtrace;
|
||||
|
||||
public function __construct(message: \string $message, tags: \array $tags, backtrace: ?\array = null)
|
||||
{
|
||||
$this->message = $message; # not escaped
|
||||
$this->tags = $tags;
|
||||
$this->timestamp = \time();
|
||||
|
||||
if ($backtrace === null)
|
||||
{
|
||||
$backtrace = \debug_backtrace();
|
||||
$key = \array_search(__FUNCTION__, \array_column($backtrace, 'function'),); # NOTE: manual search, can change with time
|
||||
$backtrace = $backtrace[$key];
|
||||
}
|
||||
$this->backtrace = $backtrace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display this event for logging
|
||||
*
|
||||
* This can be changed from configuration.
|
||||
* The EOL (end of line sequence) is automatically added at the end.
|
||||
*/
|
||||
public function display() : \string
|
||||
{
|
||||
$configuration = \core\config\GlobalConfiguration::read('class', 'core', 'log');
|
||||
$format = '[{tags}] - {date}: {message} in {file} at line {line}'; # NOTE: hard-coded fallback
|
||||
$date_format = 'Y-m-d H:i:s'; # NOTE: hard-coded fallback
|
||||
if ($configuration->exist('format'))
|
||||
{
|
||||
$format = $configuration->get('format');
|
||||
}
|
||||
if ($configuration->exist('date-format'))
|
||||
{
|
||||
$date_format = $configuration->get('date-format');
|
||||
}
|
||||
return \core\substitute(
|
||||
$format,
|
||||
array(
|
||||
'date' => \date($date_format),
|
||||
'file' => $this->backtrace['file'],
|
||||
'line' => $this->backtrace['line'],
|
||||
'message' => $this->message,
|
||||
'tags' => $this->tags,
|
||||
),
|
||||
) . \PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
85
class/core/log/Logger.class.php
Normal file
85
class/core/log/Logger.class.php
Normal file
|
@ -0,0 +1,85 @@
|
|||
<?php
|
||||
|
||||
namespace core\log;
|
||||
|
||||
/**
|
||||
* Manage server logs
|
||||
*/
|
||||
class Logger
|
||||
{
|
||||
|
||||
/**
|
||||
* Array of streams used by this logger
|
||||
*
|
||||
* @var \core\log\Stream[]
|
||||
*/
|
||||
protected \array $streams;
|
||||
|
||||
/**
|
||||
* Constructor of Logger
|
||||
*
|
||||
* @param \core\path\Path $path Path to the folder where logs manaded by this object are stored
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->streams = \array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a stream to manage
|
||||
*
|
||||
* @param \core\log\Tag $tag Tag of the stream to manage
|
||||
*
|
||||
* @throws \core\log\StreamException
|
||||
*
|
||||
* @return \core\log\Stream
|
||||
*/
|
||||
protected function add_stream(\core\log\Tag $tag) : \core\log\Stream
|
||||
{
|
||||
if (!$this->stream_exist(tag: $tag))
|
||||
{
|
||||
$this->streams[$tag] = \core\log\StreamFactory::build(tag: $tag); # NOTE: can throw \core\log\StreamException too
|
||||
return $this->streams[$tag];
|
||||
}
|
||||
\throw new \core\log\StreamException(_('the stream already exist, cannot add it', 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if stream associated to the tag already exist
|
||||
*
|
||||
* @param ?\core\log\Tag $tag Tag associated to a stream
|
||||
*
|
||||
* @param ?\core\log\Stream $stream Stream to search for
|
||||
*
|
||||
* @throws \core\log\LogException
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function stream_exist(tag: ?\core\log\Tag $tag = null, stream: ?\core\log\Stream $stream = null) : bool
|
||||
{
|
||||
if (\is_null($stream))
|
||||
{
|
||||
if (\is_null($tag))
|
||||
{
|
||||
\throw new \core\log\LogException(_("tag and stream arguments cannot be null at the same time"), 1);
|
||||
}
|
||||
|
||||
if (\in_array($tag, \array_keys($this->streams)))
|
||||
{
|
||||
return True;
|
||||
}
|
||||
return False;
|
||||
}
|
||||
if (\is_null($tag))
|
||||
{
|
||||
if (\in_array($stream, $this->streams))
|
||||
{
|
||||
return True
|
||||
}
|
||||
return False
|
||||
}
|
||||
# this will never be reached
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
61
class/core/log/Stream.class.php
Normal file
61
class/core/log/Stream.class.php
Normal file
|
@ -0,0 +1,61 @@
|
|||
<?php
|
||||
|
||||
namespace core\log;
|
||||
|
||||
/**
|
||||
* Interface to manage streaming system
|
||||
*/
|
||||
interface Stream
|
||||
{
|
||||
/**
|
||||
* Dispatcher configuration
|
||||
*
|
||||
* @var \core\config\Configuration
|
||||
*/
|
||||
protected \core\config\Configuration $configuration;
|
||||
|
||||
/**
|
||||
* Tag associated to the stream
|
||||
*
|
||||
* @var \core\log\Tag
|
||||
*/
|
||||
protected \core\log\Tag $tag;
|
||||
|
||||
/**
|
||||
* Stream constructor
|
||||
*
|
||||
* @param \core\config\Configuration $config Dispatcher configuration
|
||||
*
|
||||
* @throws \core\config\ConfigException
|
||||
*/
|
||||
public function __construct(config: \core\config\Configuration $configuration, tag: \core\log\Tag $tag);
|
||||
|
||||
/**
|
||||
* Push an event to the stream
|
||||
*
|
||||
* @param \core\log\Event $event Event to push
|
||||
*
|
||||
* @throws \core\log\StreamException
|
||||
*
|
||||
* @return \core\log\Event Event pushed
|
||||
*/
|
||||
public function push(event: \core\log\Event $event) : \core\log\Event;
|
||||
|
||||
/**
|
||||
* Connect to an existing stream running outside of PHP
|
||||
*
|
||||
* @param \core\log\StreamSettings $settings Settings to connect to the stream
|
||||
*
|
||||
* @throws \core\log\StreamException
|
||||
*/
|
||||
public function connect();
|
||||
|
||||
/**
|
||||
* Close the connection to an existing stream running outside of PHP
|
||||
*
|
||||
* @throws \core\log\StreamException
|
||||
*/
|
||||
public function close();
|
||||
}
|
||||
|
||||
?>
|
21
class/core/log/StreamException.class.php
Normal file
21
class/core/log/StreamException.class.php
Normal file
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
|
||||
namespace core\log;
|
||||
|
||||
/**
|
||||
* Exception on stream processing
|
||||
*/
|
||||
class StreamException extends \core\exception\CustomException
|
||||
{
|
||||
/**
|
||||
* default tags associated to this exception
|
||||
*
|
||||
* @var \class[]
|
||||
*/
|
||||
const TAGS = [
|
||||
\core\log\tag\Error,
|
||||
\core\log\tag\Logging,
|
||||
]
|
||||
}
|
||||
|
||||
?>
|
60
class/core/log/StreamFactory.class.php
Normal file
60
class/core/log/StreamFactory.class.php
Normal file
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
namespace core\log;
|
||||
|
||||
/**
|
||||
* Stream factory, to create stream with ease
|
||||
*/
|
||||
class StreamFactory
|
||||
{
|
||||
/**
|
||||
* Available client NOTE: to update for "vanilla" client
|
||||
*
|
||||
* @var \string[]
|
||||
*/
|
||||
const AVAILABLE_CLIENTS = [
|
||||
'\\core\\log\\client\\MQTT',
|
||||
];
|
||||
|
||||
/**
|
||||
* @throws \core\config\ConfigException
|
||||
* @throws \core\log\StreamException
|
||||
*/
|
||||
public static function build(tag: \core\log\Tag $tag) : \core\log\Stream
|
||||
{
|
||||
$configuration = \core\config\Configuration(array(
|
||||
'tags' => array(),
|
||||
'client' => array('name' => '\\core\\log\\client\\File'),
|
||||
));
|
||||
if (\core\config\GlobalConfiguration::exist('class', 'core', 'log'))
|
||||
{
|
||||
$configuration = \core\config\GlobalConfiguration::read('class' 'core', 'log');
|
||||
}
|
||||
if (!\in_array($tag->__tostring(), $configuration.get('tags')))
|
||||
{
|
||||
\throw new \core\log\StreamException(_('this stream should not be created as the admin does not want to listen to it', 1)); # WARNING: this should only be seen at [DEBUG] level
|
||||
}
|
||||
|
||||
$client = '\\core\\log\\client\\File'; # NOTE: hard-coded fallback
|
||||
if ($configuration->exist('client', 'name'))
|
||||
{
|
||||
|
||||
if (\mb_split('\\', $configuration->get('client', 'name'))[0] !== 'plugins') # WARNING: any plugin can add a client, but then the name should start with "plugins" (and then "\\foo\\Bar)"
|
||||
{
|
||||
if (!\in_array($configuration->get('client', 'name'), self::AVAILABLE_CLIENTS))
|
||||
{
|
||||
\throw new \core\log\StreamException(_('unknown client in configuration', 2));
|
||||
}
|
||||
$client = $configuration.get('client', 'name');
|
||||
}
|
||||
else
|
||||
{
|
||||
$client = $configuration.get('client', 'name');
|
||||
}
|
||||
}
|
||||
|
||||
$stream = new $client($configuration.get('client', 'settings'), $tag);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
59
class/core/log/Tag.class.php
Normal file
59
class/core/log/Tag.class.php
Normal file
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
|
||||
namespace core\log;
|
||||
|
||||
/**
|
||||
* Base class for logging tag
|
||||
*/
|
||||
class Tag
|
||||
{
|
||||
/**
|
||||
* Name of the tag
|
||||
*
|
||||
* @var \string
|
||||
*/
|
||||
protected \string $name;
|
||||
|
||||
/**
|
||||
* Constructor of Tag
|
||||
*
|
||||
* @param \string $name Name of the tag
|
||||
*/
|
||||
public function __construct(\string $name)
|
||||
{
|
||||
if (\str_contains($name, '..'))
|
||||
{
|
||||
\throw \core\log\LogException(_('tag name cannot contains .. as it could be used to write outside the main log directory', 2))
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->name = $name;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate safe id for general purpose (only [A-Z][a-Z]-_ chars)
|
||||
*
|
||||
* Replace whitespace character and non base latin character to "_".
|
||||
* Keep [A-Z][a-z]- chars as it is
|
||||
*/
|
||||
public function generate_id() : \string
|
||||
{
|
||||
return \preg_replace(
|
||||
'/[^a-zA-Z-]/gm',
|
||||
'_',
|
||||
\mb_convert_encoding(
|
||||
$this->__tostring(),
|
||||
'ASCII',
|
||||
'UTF-8',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public function __toString() : \string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
60
class/core/log/client/MQTT.class.php
Normal file
60
class/core/log/client/MQTT.class.php
Normal file
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
namespace core\log\client;
|
||||
|
||||
/**
|
||||
* MQTT stream
|
||||
*/
|
||||
class MQTT implements \core\log\Stream
|
||||
{
|
||||
/**
|
||||
* Client object from php-mqtt
|
||||
*
|
||||
* @var \PhpMqtt\Client\MqttClient
|
||||
*/
|
||||
protected \PhpMqtt\Client\MqttClient $client;
|
||||
|
||||
/**
|
||||
* Connection settings from php-mqtt
|
||||
*
|
||||
* @var \PhpMqtt\Client\ConnectionSettings
|
||||
*/
|
||||
protected \PhpMqtt\Client\ConnectionSettings $connection_settings;
|
||||
|
||||
/**
|
||||
* @throws \core\config\ConfigException
|
||||
*/
|
||||
public function __construct(config: \core\config\Configuration $configuration, tag: \core\log\Tag $tag)
|
||||
{
|
||||
$this->configuration = $configuration;
|
||||
$this->tag = $tag;
|
||||
|
||||
$client_id = 'php-mqtt-client';
|
||||
if ($this->configuration.exist('client_id'))
|
||||
{
|
||||
$client_id = $this->configuration.get('client_id'); # TODO: Check if we need to sanitize
|
||||
}
|
||||
|
||||
$this->client = new \PhpMqtt\Client\MqttClient($this->configuration('hostname'), $this->configuration.get('port'), $client_id, \PhpMqtt\Client\MqttClient::MQTT_3_1, null);
|
||||
|
||||
$this->connection_settings = new \PhpMqtt\Client\ConnectionSettings;
|
||||
if ($this->configuration.exist('auth', 'username'))
|
||||
{
|
||||
$this->connection_settings->setUsername($this->configuration.get('auth', 'username'));
|
||||
}
|
||||
if ($this->configuration.exist('auth', 'password'))
|
||||
{
|
||||
$this->connection_settings->setPassword($this->configuration.get('auth', 'password'));
|
||||
}
|
||||
}
|
||||
|
||||
public function push(event: \core\log\Event $event) : \core\log\Event
|
||||
{
|
||||
$this->client->connect($this->connection_settings, True);
|
||||
$this->client->publish($tag->generate_id(), $event->display(), \PhpMqtt\Client\MqttClient::QO5_EXACTLY_ONCE);
|
||||
$this->client->loop(True, True);
|
||||
$this->client->disconnect()
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
48
class/core/log/client/file.class.php
Normal file
48
class/core/log/client/file.class.php
Normal file
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
namespace core\log\client;
|
||||
|
||||
/**
|
||||
* Store stream in files
|
||||
*/
|
||||
class File
|
||||
{
|
||||
/**
|
||||
* File where event are stored
|
||||
*
|
||||
* @var \resource
|
||||
*/
|
||||
protected \resource $file;
|
||||
|
||||
/**
|
||||
* @throws \core\config\ConfigException
|
||||
* @throws \core\log\LogException
|
||||
*/
|
||||
public function __construct(config: \core\config\Configuration $configuration, tag: \core\log\Tag $tag)
|
||||
{
|
||||
$this->configuration = $configuration;
|
||||
$this->tag = $tag;
|
||||
|
||||
$path = $configuration.get('path'); # WARNING: must be absolute path
|
||||
$path->safe_append($tag->generate_id());
|
||||
|
||||
$this->file = \fopen($path->__tostring(), 'a');
|
||||
|
||||
if (!$this->file)
|
||||
{
|
||||
\throw new \core\log\LogException(_('cannot open log file'), 1);
|
||||
}
|
||||
}
|
||||
|
||||
public funtion push(event: \core\log\Event $event) : \core\log\Event
|
||||
{
|
||||
if (!\flock($this->file, \LOCK_EX))
|
||||
{
|
||||
\throw new \core\log\LogException(_('cannot guarantee that the file is not already being edited'));
|
||||
}
|
||||
\fwrite($this->file, $event->display());
|
||||
\flock($this->file, LOCK_UN);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
46
class/core/log/client/redis.class.php
Normal file
46
class/core/log/client/redis.class.php
Normal file
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
|
||||
namespace core\log\client;
|
||||
|
||||
/**
|
||||
* Redis stream (only https://github.com/phpredis/phpredis for now)
|
||||
*/
|
||||
class Redis
|
||||
{
|
||||
/**
|
||||
* Client object for redis
|
||||
*
|
||||
* @var \Redis
|
||||
*/
|
||||
protected \Redis $client;
|
||||
|
||||
/**
|
||||
* @throws \core\config\ConfigException
|
||||
*/
|
||||
public function __construct(config: \core\config\Configuration $configuration, tag: \core\log\Tag $tag)
|
||||
{
|
||||
$this->configuration = $configuration;
|
||||
$this->tag = $tag;
|
||||
$config = array(
|
||||
'host' => $this->configuration.get('hostname'),
|
||||
'port' => $this->configuration.get('port'), // -1 if socket
|
||||
);
|
||||
if ($this->configuration.exist('auth'))
|
||||
{
|
||||
$config['auth'] = array();
|
||||
if ($this->configuration.exist('auth', 'username'))
|
||||
{
|
||||
$config['auth'][] = $this->configuration.get('auth', 'username');
|
||||
}
|
||||
$config['auth'][] = $this->configuration.get('auth', 'password');
|
||||
}
|
||||
$this->client = new \Redis($config);
|
||||
}
|
||||
|
||||
public function push(event: \core\log\Event $event) : \core\log\Event
|
||||
{
|
||||
$this->client->rpush($tag->generate_id(), $event->display());
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
152
func/core/utils.class.php
Normal file
152
func/core/utils.class.php
Normal file
|
@ -0,0 +1,152 @@
|
|||
<?php
|
||||
|
||||
namespace core;
|
||||
|
||||
/**
|
||||
* convert nearly anything to a string (not protected for html, use html_special_chars)
|
||||
*
|
||||
* @param \mixed $var Variable to convert
|
||||
*
|
||||
* @return \string string conversion result
|
||||
*/
|
||||
function display(\mixed $var) : \string
|
||||
{
|
||||
if (\is_string($var))
|
||||
{
|
||||
return $var;
|
||||
}
|
||||
if (\is_bool($var))
|
||||
{
|
||||
return ($var ? 'True' : 'False');
|
||||
}
|
||||
if (\is_int($var) || \is_float($var))
|
||||
{
|
||||
return (string) $var;
|
||||
}
|
||||
if (\is_null($var))
|
||||
{
|
||||
return 'null';
|
||||
}
|
||||
if (\is_resource($var))
|
||||
{
|
||||
return \get_resource_type($var);
|
||||
}
|
||||
if (\is_array($var))
|
||||
{
|
||||
$ret = 'array (';
|
||||
foreach ($var as $key => $el)
|
||||
{
|
||||
$ret .= ' ' . \core\display($key) . ' => ' . \core\display($el);
|
||||
}
|
||||
return $ret . ' )';
|
||||
}
|
||||
if (\is_object($var))
|
||||
{
|
||||
if (!\method_exists($var, '__toString'))
|
||||
{
|
||||
return \get_class($var) . ' : ' . \core\display(\core\table($var, 3)); // NOTE: arbitrary hard-coded recursion value
|
||||
}
|
||||
else
|
||||
{
|
||||
return $var->__toString();
|
||||
}
|
||||
}
|
||||
if (\is_callable($var))
|
||||
{
|
||||
if ($var instanceOf \Closure)
|
||||
{
|
||||
return 'a closure';
|
||||
}
|
||||
return 'unknown callable';
|
||||
}
|
||||
if (\is_iterable($var))
|
||||
{
|
||||
return 'iterable';
|
||||
}
|
||||
return 'cannot convert the variable that should be here to a string';
|
||||
}
|
||||
|
||||
/**
|
||||
* substitution helper
|
||||
*
|
||||
* substitute {element} with element for each element in token
|
||||
* Not safe to display without sanitization on a webpage
|
||||
*
|
||||
* @param \string $content
|
||||
*
|
||||
* @param \array $tokens
|
||||
*
|
||||
* @return \string
|
||||
*/
|
||||
function substitute(\string $content, \array $tokens) : \string
|
||||
{
|
||||
$parts = \preg_split('/({(?:\\}|[^\\}])+})/Um', $content, -1, \PREG_SPLIT_DELIM_CAPTURE);
|
||||
|
||||
if (\count($tokens) > 0)
|
||||
{
|
||||
foreach ($tokens as $name => $value)
|
||||
{
|
||||
if (\in_array('{' . $name . '}', $parts))
|
||||
{
|
||||
foreach (\array_keys($parts, '{' . $name . '}') as $key)
|
||||
{
|
||||
$parts[$key] = \core\display($value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return \implode($parts);
|
||||
}
|
||||
|
||||
/**
|
||||
* convert any object into a table
|
||||
*
|
||||
* @param \object $object Object to convert
|
||||
*
|
||||
* @param \int $depth Depth of the recursion if there is an object. -1 for infinite, 0 for no recursion.
|
||||
* Default to 0.
|
||||
*
|
||||
* @return \array
|
||||
*/
|
||||
function table(\object $object, \int $depth = 0) : \array
|
||||
{
|
||||
if (\in_array(\core\Base::class, \class_uses($object)))
|
||||
{
|
||||
return $object->table();
|
||||
}
|
||||
|
||||
$attributes = [];
|
||||
|
||||
if ($depth < -1)
|
||||
{
|
||||
throw new \Exception('Depth cannot be below -1');
|
||||
}
|
||||
|
||||
foreach (\array_keys(\get_class_vars(\get_class($object))) as $attribute)
|
||||
{
|
||||
if (\is_object($object->$attribute))
|
||||
{
|
||||
if ($depth === 0)
|
||||
{
|
||||
$attributes[$attribute] = $object->$attribute;
|
||||
}
|
||||
else if ($depth > 0)
|
||||
{
|
||||
$attributes[$attribute] = \core\table($object->$attribute, $depth - 1);
|
||||
}
|
||||
else // depth === -1
|
||||
{
|
||||
$attributes[$attribute] = \core\table($object->$attribute, $depth);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$attributes[$attribute] = $object->$attribute;
|
||||
}
|
||||
}
|
||||
|
||||
return $attributes;
|
||||
}
|
||||
|
||||
?>
|
Loading…
Reference in a new issue