Add the main class

2.x
Julien Rosset 4 years ago
parent f4df951d49
commit 13f5b1d9c9

@ -5,7 +5,8 @@
"minimum-stability": "stable",
"require": {
"php": "~7.4 || ~8.0"
"php": "~7.4 || ~8.0",
"jrosset/singleton": "^1.0"
},
"autoload": {
"psr-4": {

46
composer.lock generated

@ -4,8 +4,50 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "2ab889d9a2d5d5b28b48164a73b529b2",
"packages": [ ],
"content-hash": "2f92e58b7bd7a162f29ef03fe018cec6",
"packages": [
{
"name": "jrosset/singleton",
"version": "v1.0.0",
"source": {
"type": "git",
"url": "https://git.jrosset.ovh/jrosset/PhpSingleton",
"reference": "0fccc3b4b1c46e42656e84e264555e57bf2f27cd"
},
"require": {
"php": "~7.4 || ~8.0"
},
"type": "library",
"autoload": {
"psr-4": {
"jrosset\\": "src/"
},
"exclude-from-classmap": [
"tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"CC-BY-4.0"
],
"authors": [
{
"name": "Julien Rosset",
"email": "jul.rosset@gmail.com"
}
],
"description": "PHP Trait to implements the singleton design pattern",
"homepage": "https://git.jrosset.ovh/jrosset/PhpSingleton",
"support": {
"docs": "https://git.jrosset.ovh/jrosset/PhpSingleton/wiki",
"email": "jul.rosset@gmail.com",
"issues": "https://git.jrosset.ovh/jrosset/PhpSingleton/issues",
"source": "https://git.jrosset.ovh/jrosset/PhpSingleton",
"wiki": "https://git.jrosset.ovh/jrosset/PhpSingleton/wiki"
},
"time": "2021-09-03T10:57:30+00:00"
}
],
"packages-dev": [ ],
"aliases": [ ],
"minimum-stability": "stable",

@ -0,0 +1,235 @@
<?php
namespace jrosset\EnvReader;
use DateTime;
use DateTimeInterface;
use Exception;
use jrosset\Singleton\TSingleton;
use RangeException;
use UnexpectedValueException;
/**
* Utility class to get informations from ENV file
*
* You should overwrite {@see Env::PATH_ENV} to set ENV file path<br>
* Overwrite {@see Env::initProperties()} to set initial properties
*/
abstract class Env {
use TSingleton;
/**
* ENV file path
*/
protected const PATH_ENV = '.env';
/**
* @var string[] Current properties, read from ENV file (&lt;property name&gt; => &lt;property value&gt;)<br>
* <b>NOTE:</b> All properties' name are stored in upper-case
*/
private array $properties;
/**
* Initialize initial properties then read ENV file
*
* @throws Exception If ENV can't be read
*/
protected function __construct () {
$this->properties = array_change_key_case($this->initProperties(), CASE_UPPER);
$this->readEnv();
}
/**
* Get all properties
*
* @return string[] Get all properties (properties' name are in upper case)
*/
public function getProperties (): array {
return $this->properties;
}
/**
* Check if a property id defined
*
* @param string $name The property name
*
* @return bool Is the property defined ?
*/
public function hasProperty (string $name): bool {
return array_key_exists(mb_strtoupper($name), $this->getProperties());
}
/**
* Get a property value
*
* @param string $name The property name
* @param string|null $default The default value if property is not set
* Raise an exception if property is not set AND $default is Null
*
* @return string The property value
*
* @throws UnexpectedValueException If property is not set AND $default is Null
*/
public function getProperty (string $name, ?string $default = null): string {
$name = mb_strtoupper($name);
if (!$this->hasProperty($name)) {
if ($default === null) {
throw new UnexpectedValueException('The "' . $name . '" property is not set');
}
return $default;
}
return $this->properties[$name];
}
/**
* Get a property value as a boolean
*
* @param string $name The property name
* @param bool|null $default The default value if property is not set
* Raise an exception if property is not set AND $default is Null
*
* @return bool The property value
*
* @throws UnexpectedValueException If property is not set AND $default is Null
* @throws RangeException If the property can't be cast to a boolean
*
* @noinspection PhpUnused
*/
public function getPropertyAsBool (string $name, ?bool $default = null): bool {
switch ($value = $this->getProperty($name, $default === null ? null : ($default === true ? '1' : '0'))) {
case '1':
case 'true':
return true;
case '0':
case 'false':
return false;
default:
throw new RangeException('The "' . $name . '" property is not a valid boolean : ' . $value);
}
}
/**
* Get a property value as an integer
*
* @param string $name The property name
* @param int|null $default The default value if property is not set
* Raise an exception if property is not set AND $default is Null
*
* @return int The property value
*
* @throws UnexpectedValueException If property is not set AND $default is Null
* @throws RangeException If the property can't be cast to an integer
*
* @noinspection PhpUnused
*/
public function getPropertyAsInt (string $name, ?int $default = null): int {
$value = $this->getProperty($name, $default === null ? null : (string)$default);
if (preg_match('#^\s*(?<value>[0-9]+)\s*$#', $value, $match) !== 1) {
throw new RangeException('The "' . $name . '" property is not a valid integer : ' . $value);
}
return (int)$match['value'];
}
/**
* Get a property value as a float value (the decimal separator is dot)
*
* @param string $name The property name
* @param float|null $default The default value if property is not set
* Raise an exception if property is not set AND $default is Null
*
* @return float The property value
*
* @throws UnexpectedValueException If property is not set AND $default is Null
* @throws RangeException If the property can't be cast to a float value
*
* @noinspection PhpUnused
*/
public function getPropertyAsReal (string $name, ?float $default = null): float {
$value = $this->getProperty($name, $default === null ? null : (string)$default);
if (preg_match('#^\s*(?<value>[0-9]+(?:\.[0-9]+)?)\s*$#', $value, $match) !== 1) {
throw new RangeException('The "' . $name . '" property is not a valid float value : ' . $value);
}
return (float)$match['value'];
}
/**
* Get a property value as a date and time ({@link https://www.php.net/manual/book.datetime.php DateTime})
*
* The date <b>must</b> respect {@link https://datatracker.ietf.org/doc/html/rfc3339 RFC339} norm
*
* @param string $name The property name
* @param DateTime|null $default The default value if property is not set
* Raise an exception if property is not set AND $default is Null
*
* @return DateTime The property value
*
* @throws UnexpectedValueException If property is not set AND $default is Null
* @throws RangeException If the property can't be cast to a date and time
*
* @noinspection PhpUnused
*/
public function getPropertyAsDateTime (string $name, ?DateTime $default = null): DateTime {
$value = DateTime::createFromFormat(
DateTimeInterface::RFC3339,
$this->getProperty($name, $default === null ? null : $default->format(DateTimeInterface::RFC3339))
);
$errors = DateTime::getLastErrors();
if ($errors !== false) {
$messages = [];
if (($nb_error = $errors['error_count']) > 0) {
foreach ($errors['errors'] as $idx => $error) {
$messages[] = 'ERREUR #' . $idx . ' : ' . $error;
}
}
if (($nb_warning = $errors['warning_count']) > 0) {
foreach ($errors['warnings'] as $idx => $warning) {
$messages[] = 'ALERTE #' . $idx . ' : ' . $warning;
}
}
if ($nb_error + $nb_warning > 0) {
throw new RangeException('The "' . $name . '" property is not a valid date and time : ' . $value . "\n\n" . implode("\n", $messages));
}
}
if ($value === false) {
throw new RangeException('The "' . $name . '" property is not a valid date and time : ' . $value);
}
return $value;
}
/**
* Initialize initial properties
*
* @return string[] Properties initial values (&lt;property name&gt; => &lt;property value&gt;)
*/
protected function initProperties (): array {
return [
'APP_DEV' => 0,
];
}
/**
* Read the ENV file
*
* @throws Exception If ENV can't be read
*/
private function readEnv (): void {
if (!file_exists(static::PATH_ENV)) {
return;
}
$lines = file(static::PATH_ENV, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
if ($lines === false) {
throw new Exception('Unable to read environment file "' . static::PATH_ENV . '"');
}
foreach ($lines as $line) {
if (preg_match('/^\s*(?:(?<comment>#).+|(?<key>[a-zA-Z0-9_-]+)=(?<value>.+))$/i', $line, $match) !== 1) {
continue; // Ligne invalide
}
if (isset($match['comment']) && trim($match['comment']) !== '') {
continue; // Commentaire
}
$this->properties[mb_strtoupper($match['key'])] = $match['value'];
}
}
}
Loading…
Cancel
Save