Add src files for CoreLibs composer all package

This commit is contained in:
Clemens Schwaighofer
2023-02-16 12:47:43 +09:00
commit cfcd601b3a
76 changed files with 25404 additions and 0 deletions

291
src/Create/Email.php Normal file
View File

@@ -0,0 +1,291 @@
<?php
/*
* Create email class
*/
declare(strict_types=1);
namespace CoreLibs\Create;
/**
* sending simple text emails
*/
class Email
{
/** @var array<string> allowed list for encodings that can do KV folding */
private static $encoding_kv_allowed = [
'UTF-8',
'EUC-JP',
'SJIS',
'SJIS-win',
'ISO-2022-JP',
'ISO-2022-JP-MS',
'JIS',
'JIS-ms',
];
/** @var string normaly this does not need to be changed */
private static $mb_convert_kana_mode = 'KV';
/**
* create mime encoded email part for to/from emails.
* If encoding is not UTF-8 it will convert the email name to target encoding
* FROM UTF-8
* Source data is ALWAYS seen as utf-8
*
* @param string $email E-Mail address
* @param string $email_name Name for the email address, in UTF-8, if not set, empty
* @param string $encoding Encoding, if not set UTF-8
* @param bool $kv_folding If set to true and a valid encoding, do KV folding
* @return string Correctly encoded and build email string
*/
public static function encodeEmailName(
string $email,
string $email_name = '',
string $encoding = 'UTF-8',
bool $kv_folding = false
): string {
if (empty($email_name)) {
return $email;
}
// if encoding is not UTF-8 then we convert
if ($encoding != 'UTF-8') {
$email_name = mb_convert_encoding($email_name, $encoding, 'UTF-8');
}
$email_name =
mb_encode_mimeheader(
in_array($encoding, self::$encoding_kv_allowed) && $kv_folding ?
mb_convert_kana(
$email_name,
self::$mb_convert_kana_mode,
$encoding
) :
$email_name,
$encoding
);
return '"' . $email_name . '" '
. '<' . (string)$email . '>';
}
/**
* Subject/Body replace sub function
*
* @param string $subject Subject string, in UTF-8
* @param string $body Body string, in UTF-8
* @param array<string,string> $replace Replace the array as key -> value, in UTF-8
* @param string $encoding Encoding for subject encode mime header
* @param bool $kv_folding If set to true and a valid encoding,
* do KV folding
* @return array<string> Pos 0: Subject, Pos 1: Body
*/
private static function replaceContent(
string $subject,
string $body,
array $replace,
string $encoding,
bool $kv_folding
): array {
foreach (['subject', 'body'] as $element) {
$$element = str_replace(
array_map(
function ($key) {
return '{' . $key . '}';
},
array_keys($replace)
),
array_values($replace),
$$element
);
}
// if encoding is NOT UTF-8 convert to target
if ($encoding != 'UTF-8') {
$subject = mb_convert_encoding($subject, $encoding, 'UTF-8');
$body = mb_convert_encoding($body, $encoding, 'UTF-8');
}
// we need to encodde the subject
$subject = mb_encode_mimeheader(
in_array($encoding, self::$encoding_kv_allowed) && $kv_folding ?
// for any non UTF-8 encoding convert kana
mb_convert_kana(
$subject,
self::$mb_convert_kana_mode,
$encoding
) :
$subject,
$encoding
);
return [$subject, $body];
}
/**
* Send plain text email with possible to replace subject/body data
* either global or per to email set.
* replace to tags are in {} in the subject or body
*
* @param string $subject Mail subject, mandatory, in UTF-8
* @param string $body Mail body, mandatory, in UTF-8
* @param string $from_email From email, mandatory
* @param string $from_name From email name, in UTF-8
* if empty '' then not set
* @param array<mixed> $send_to_emails to email or array for email/replace
* If array: name/email/replace[key,value]
* name and replace must be in UTF-8
* At least one must be set
* @param array<string,string> $replace_content Subject/Body replace as
* search -> replace, in UTF-8
* @param string $encoding E-Mail encoding, default UTF-8
* @param bool $kv_folding If set to true and a valid encoding,
* do KV folding
* @param bool $test test flag, default off
* @param \CoreLibs\Debug\Logging|null $log Logging class,
* only used if test flag is true
* @return int 2 test only, no sent
* 1 for ok,
* 0 for send not ok
* -1 for nothing set (emails, subject, body)
* -2 for empty to list
* -3 encoding target not valid or not installed
*/
public static function sendEmail(
string $subject,
string $body,
string $from_email,
string $from_name,
array $send_to_emails,
array $replace_content = [],
string $encoding = 'UTF-8',
bool $kv_folding = false,
bool $test = false,
?\CoreLibs\Debug\Logging $log = null
): int {
/** @var array<string> */
$to_emails = [];
/** @var array<string,array<string,string>> */
$to_replace = [];
/** @var string */
$out_subject = $subject;
/** @var string */
$out_body = $body;
// check basic set
if (empty($subject) || empty($body) || empty($from_email)) {
return -1;
}
if (
$encoding != 'UTF-8' &&
!in_array($encoding, mb_list_encodings())
) {
return -3;
}
// if not one valid to, abort
foreach ($send_to_emails as $to_email) {
// to_email can be string, then only to email
// else expect 'email' & 'name'
if (
is_array($to_email) &&
isset($to_email['email'])
) {
$_to_email = self::encodeEmailName(
$to_email['email'],
$to_email['name'] ?? '',
$encoding,
$kv_folding
);
$to_emails[] = $_to_email;
// if we have to replacement, this override replace content
if (isset($to_email['replace']) && count($to_email['replace'])) {
// merge with original replace content,
// to data will override original data
$to_replace[$_to_email] = array_merge(
$replace_content,
$to_email['replace']
);
}
} elseif (is_string($to_email)) {
$to_emails[] = $to_email;
}
}
if (!count($to_emails)) {
return -2;
}
// the email headers needed
$headers = [
'From' => self::encodeEmailName($from_email, $from_name, $encoding),
'Content-type' => "text/plain; charset=" . $encoding,
'MIME-Version' => "1.0",
];
// if we have a replace string, we need to do replace run
// only if there is no dedicated to replace
// also run replace if there is nothing to replace at all
// this will mime encode the subject
if (!count($to_replace)) {
list($out_subject, $out_body) = self::replaceContent(
$subject,
$body,
$replace_content,
$encoding,
$kv_folding
);
}
$mail_delivery_status = 1;
// send the email
foreach ($to_emails as $to_email) {
// default mail status is success
$mail_status = true;
// if there is a to replace, if not use the original replace content
if (count($to_replace)) {
$_replace = [];
if (!empty($to_replace[$to_email])) {
$_replace = $to_replace[$to_email];
} elseif (count($replace_content)) {
$_replace = $replace_content;
}
if (count($_replace)) {
list($out_subject, $out_body) = self::replaceContent(
$subject,
$body,
$_replace,
$encoding,
$kv_folding
);
}
}
// if we are in test mode, do not send an email and set status to 2
if ($test === false) {
$mail_status = mail($to_email, $out_subject, $out_body, $headers);
} else {
$mail_delivery_status = 2;
}
// log if an log instance exists
if ($log instanceof \CoreLibs\Debug\Logging) {
// build debug strings: convert to UTF-8 if not utf-8
$log->debug('SEND EMAIL', 'HEADERS: ' . $log->prAr($headers) . ', '
. 'ENCODING: ' . $encoding . ', '
. 'KV FOLDING: ' . $log->prBl($kv_folding) . ', '
. 'TO: ' . $to_email . ', '
. 'SUBJECT: ' . $out_subject . ', '
. 'BODY: ' . ($encoding == 'UTF-8' ?
$out_body :
mb_convert_encoding($out_body, 'UTF-8', $encoding)));
$log->debug('SEND EMAIL JSON', json_encode([
'encoding' => $encoding,
'kv_folding' => $kv_folding,
'header' => $headers,
'to' => $to_email,
'subject' => $out_subject,
'body' => ($encoding == 'UTF-8' ?
$out_body :
mb_convert_encoding($out_body, 'UTF-8', $encoding))
]) ?: '{}');
}
if (!$mail_status) {
$mail_delivery_status = 0;
}
}
return $mail_delivery_status;
}
}
// __END__

122
src/Create/Hash.php Normal file
View File

@@ -0,0 +1,122 @@
<?php
/*
* hash wrapper functions for old problem fixes
*/
declare(strict_types=1);
namespace CoreLibs\Create;
class Hash
{
public const STANDARD_HASH_LONG = 'ripemd160';
public const STANDARD_HASH_SHORT = 'adler32';
/**
* checks php version and if >=5.2.7 it will flip the string
* can return empty string if none of string sets work
* hash returns false
* preg_replace fails for older php version
* Use __hash with crc32b or hash('crc32b', ...) for correct output
*
* @param string $string string to crc
* @return string crc32b hash (old type)
*/
public static function __crc32b(string $string): string
{
// do normal hash crc32b
$string = hash('crc32b', $string);
// if bigger than 5.2.7, we need to "unfix" the fix
if (\CoreLibs\Check\PhpVersion::checkPHPVersion('5.2.7')) {
// flip it back to old (two char groups)
$string = preg_replace("/^([a-z0-9]{2})([a-z0-9]{2})([a-z0-9]{2})([a-z0-9]{2})$/", "$4$3$2$1", $string);
}
if (!is_string($string)) {
$string = '';
}
return $string;
}
/**
* replacement for __crc32b call
*
* @param string $string string to hash
* @param bool $use_sha use sha instead of crc32b (default false)
* @return string hash of the string
*/
public static function __sha1Short(string $string, bool $use_sha = false): string
{
if ($use_sha) {
// return only the first 9 characters
return substr(hash('sha1', $string), 0, 9);
} else {
return self::__crc32b($string);
}
}
/**
* replacemend for __crc32b call (alternate)
* defaults to adler 32
* allowed crc32b, adler32, fnv132, fnv1a32, joaat
* all that create 8 char long hashes
*
* @param string $string string to hash
* @param string $hash_type hash type (default adler32)
* @return string hash of the string
*/
public static function __hash(
string $string,
string $hash_type = self::STANDARD_HASH_SHORT
): string {
if (
!in_array(
$hash_type,
['crc32b', 'adler32', 'fnv132', 'fnv1a32', 'joaat']
)
) {
$hash_type = 'adler32';
}
return hash($hash_type, $string);
}
/**
* Wrapper function for standard long hashd
*
* @param string $string String to be hashed
* @return string Hashed string
*/
public static function __hashLong(string $string): string
{
return hash(self::STANDARD_HASH_LONG, $string);
}
/**
* create a unique id with the standard hash type defined in __hash
*
* @return string Unique ID with fixed length of 8 characters
* @deprecated Use \CoreLibs\Create\Uids::uniqIdShort() instead
*/
public static function __uniqId(): string
{
trigger_error('Method ' . __METHOD__ . ' is deprecated, '
. '\CoreLibs\Create\Uids::uniqIdShort() class', E_USER_DEPRECATED);
return \CoreLibs\Create\Uids::uniqIdShort();
}
/**
* create a unique id with the standard long hash type
* defined in __hashLong
*
* @return string Unique ID with length of current default long hash
* @deprecated Use \CoreLibs\Create\Uids::uniqIdLong() instead
*/
public static function __uniqIdLong(): string
{
trigger_error('Method ' . __METHOD__ . ' is deprecated, '
. '\CoreLibs\Create\Uids::uniqIdLong() class', E_USER_DEPRECATED);
return \CoreLibs\Create\Uids::uniqIdLong();
}
}
// __END__

121
src/Create/RandomKey.php Normal file
View File

@@ -0,0 +1,121 @@
<?php
/*
* random key functions
*/
declare(strict_types=1);
namespace CoreLibs\Create;
class RandomKey
{
// key generation
/** @var string */
private static $key_range = '';
/** @var int */
private static $one_key_length;
/** @var int */
private static $key_length = 4; // default key length
/** @var int */
private static $max_key_length = 256; // max allowed length
/**
* if launched as class, init random key data first
*/
public function __construct()
{
$this->initRandomKeyData();
}
/**
* sets the random key range with the default values
*
* @return void has no return
*/
private static function initRandomKeyData()
{
// random key generation base string
self::$key_range = join('', array_merge(
range('A', 'Z'),
range('a', 'z'),
range('0', '9')
));
self::$one_key_length = strlen(self::$key_range);
}
/**
* validates they key length for random string generation
*
* @param int $key_length key length
* @return bool true for valid, false for invalid length
*/
private static function validateRandomKeyLenght(int $key_length): bool
{
if (
is_numeric($key_length) &&
$key_length > 0 &&
$key_length <= self::$max_key_length
) {
return true;
} else {
return false;
}
}
/**
* sets the key length and checks that they key given is valid
* if failed it will not change the default key length and return false
*
* @param int $key_length key length
* @return bool true/false for set status
*/
public static function setRandomKeyLength(int $key_length): bool
{
// only if valid int key with valid length
if (self::validateRandomKeyLenght($key_length) === true) {
self::$key_length = $key_length;
return true;
} else {
return false;
}
}
/**
* get the current set random key length
*
* @return int Current set key length
*/
public static function getRandomKeyLength(): int
{
return self::$key_length;
}
/**
* creates a random key based on the key_range with key_length
* if override key length is set, it will check on valid key and use this
* this will not set the class key length variable
*
* @param int $key_length key length override, -1 for use default
* @return string random key
*/
public static function randomKeyGen(int $key_length = -1): string
{
// init random key strings if not set
if (!is_numeric(self::$one_key_length)) {
self::initRandomKeyData();
}
$use_key_length = 0;
// only if valid int key with valid length
if (self::validateRandomKeyLenght($key_length) === true) {
$use_key_length = $key_length;
} else {
$use_key_length = self::$key_length;
}
// create random string
$random_string = '';
for ($i = 1; $i <= $use_key_length; $i++) {
$random_string .= self::$key_range[random_int(0, self::$one_key_length - 1)];
}
return $random_string;
}
}

371
src/Create/Session.php Normal file
View File

@@ -0,0 +1,371 @@
<?php
/*
* AUTHOR: Clemens Schwaighofer
* DESCRIPTION:
* start a php sesseion
* name can be given via startSession parameter
* if not set tries to read $SET_SESSION_NAME from global
* else will use default set in php.ini
*/
declare(strict_types=1);
namespace CoreLibs\Create;
class Session
{
/** @var string list for errors */
private $session_intern_error_str = '';
/**
* Start session
* startSession should be called for complete check
* If this is called without any name set before the php.ini name is
* used.
*
* @return void
*/
protected function startSessionCall(): void
{
session_start();
}
/**
* init a session, if array is empty or array does not have session_name set
* then no auto init is run
*
* @param string $session_name if set and not empty, will start session
*/
public function __construct(string $session_name = '')
{
if (!empty($session_name)) {
$this->startSession($session_name);
}
}
/**
* check if we are in CLI, we set this, so we can mock this
* Not this is just a wrapper for the static System::checkCLI call
*
* @return bool True if we are in a CLI enviroment, or false for everything else
*/
public function checkCliStatus(): bool
{
return \CoreLibs\Get\System::checkCLI();
}
/**
* Set session name call. If not valid session name, will return false
*
* @param string $session_name A valid string for session name
* @return bool True if session name is valid,
* False if not
*/
public function setSessionName(string $session_name): bool
{
if (!$this->checkValidSessionName($session_name)) {
return false;
}
session_name($session_name);
return true;
}
/**
* Return set error string, empty if none set
* Error strings are only set in the startSession method
*
* @return string Last error string
*/
public function getErrorStr(): string
{
return $this->session_intern_error_str;
}
/**
* check if session name is valid
*
* As from PHP 8.1/8.0/7.4 error
* INVALID CHARS: =,; \t\r\n\013\014
* NOTE: using . will fail even thought valid
* we allow only alphanumeric with - (dash) and 1 to 128 characters
*
* @param string $session_name any string, not null
* @return bool True for valid, False for invalid
*/
public static function checkValidSessionName(string $session_name): bool
{
// check
if (
// must only have those
!preg_match('/^[-a-zA-Z0-9]{1,128}$/', $session_name) ||
// cannot be only numbers
preg_match('/^[0-9]+$/', $session_name)
) {
return false;
}
return true;
}
/**
* start session with given session name if set
* aborts on command line or if sessions are not enabled
* also aborts if session cannot be started
* On sucess returns the session id
*
* @param string|null $session_name
* @return string|bool
*/
public function startSession(?string $session_name = null)
{
// we can't start sessions on command line
if ($this->checkCliStatus()) {
$this->session_intern_error_str = '[SESSION] No sessions in php cli';
return false;
}
// if session are OFF
if ($this->getSessionStatus() === PHP_SESSION_DISABLED) {
$this->session_intern_error_str = '[SESSION] Sessions are disabled';
return false;
}
// session_status
// initial the session if there is no session running already
if (!$this->checkActiveSession()) {
// if session name is emtpy, check if there is a global set
// this is a deprecated fallback
$session_name = $session_name ?? $GLOBALS['SET_SESSION_NAME'] ?? '';
// DEPRECTED: constant SET_SESSION_NAME is no longer used
// if set, set special session name
if (!empty($session_name)) {
// invalid session name, abort
if (!$this->checkValidSessionName($session_name)) {
$this->session_intern_error_str = '[SESSION] Invalid session name: ' . $session_name;
return false;
}
$this->setSessionName($session_name);
}
// start session
$this->startSessionCall();
}
// if we still have no active session
if (!$this->checkActiveSession()) {
$this->session_intern_error_str = '[SESSION] Failed to activate session';
return false;
}
if (false === ($session_id = $this->getSessionId())) {
$this->session_intern_error_str = '[SESSION] getSessionId did not return a session id';
}
return $session_id;
}
/**
* get current set session id or false if none started
*
* @return string|bool
*/
public function getSessionId()
{
return session_id();
}
/**
* get set session name or false if none started
*
* @return string|bool
*/
public function getSessionName()
{
return session_name();
}
/**
* Checks if there is an active session.
* Does not check if we can have a session
*
* @return bool True if there is an active session, else false
*/
public function checkActiveSession(): bool
{
if ($this->getSessionStatus() === PHP_SESSION_ACTIVE) {
return true;
} else {
return false;
}
}
/**
* unlock the session file, so concurrent AJAX requests can be done
* NOTE: after this has been called, no changes in _SESSION will be stored
* NOTE: a new session with a different name can be started after this one is called
* if problem, run ob_flush() and flush() too
*
* @return bool True und sucess, false on failure
*/
public function writeClose(): bool
{
return session_write_close();
}
/**
* Proper destroy a session
* - unset the _SESSION array
* - unset cookie if cookie on and we have not strict mode
* - destroy session
*
* @return bool
*/
public function sessionDestroy(): bool
{
$_SESSION = [];
if (
ini_get('session.use_cookies') &&
!ini_get('session.use_strict_mode')
) {
$session_name = $this->getSessionName();
if ($session_name === false) {
$session_name = '';
}
$params = session_get_cookie_params();
setcookie(
(string)$session_name,
'',
time() - 42000,
$params['path'],
$params['domain'],
$params['secure'],
$params['httponly']
);
}
return session_destroy();
}
/**
* get session status
* PHP_SESSION_DISABLED if sessions are disabled.
* PHP_SESSION_NONE if sessions are enabled, but none exists.
* PHP_SESSION_ACTIVE if sessions are enabled, and one exists.
*
* https://www.php.net/manual/en/function.session-status.php
*
* @return int See possible return int values above
*/
public function getSessionStatus(): int
{
return session_status();
}
// _SESSION set/unset methods
/**
* unset all _SESSION entries
*
* @return void
*/
public function unsetAllS(): void
{
foreach (array_keys($_SESSION ?? []) as $name) {
unset($_SESSION[$name]);
}
}
/**
* set _SESSION entry 'name' with any value
*
* @param string|int $name array name in _SESSION
* @param mixed $value value to set (can be anything)
* @return void
*/
public function setS($name, $value): void
{
$_SESSION[$name] = $value;
}
/**
* get _SESSION 'name' entry or empty string if not set
*
* @param string|int $name value key to get from _SESSION
* @return mixed value stored in _SESSION
*/
public function getS($name)
{
return $_SESSION[$name] ?? '';
}
/**
* Check if a name is set in the _SESSION array
*
* @param string|int $name Name to check for
* @return bool True for set, False fornot set
*/
public function issetS($name): bool
{
return isset($_SESSION[$name]);
}
/**
* unset one _SESSION entry 'name' if exists
*
* @param string|int $name _SESSION key name to remove
* @return void
*/
public function unsetS($name): void
{
if (isset($_SESSION[$name])) {
unset($_SESSION[$name]);
}
}
// set/get below
// ->var = value;
/**
* Undocumented function
*
* @param string|int $name
* @param mixed $value
* @return void
*/
public function __set($name, $value): void
{
$_SESSION[$name] = $value;
}
/**
* Undocumented function
*
* @param string|int $name
* @return mixed
*/
public function __get($name)
{
if (isset($_SESSION[$name])) {
return $_SESSION[$name];
}
}
/**
* Undocumented function
*
* @param string|int $name
* @return bool
*/
public function __isset($name): bool
{
return isset($_SESSION[$name]);
}
/**
* Undocumented function
*
* @param string|int $name
* @return void
*/
public function __unset($name): void
{
if (isset($_SESSION[$name])) {
unset($_SESSION[$name]);
}
}
}
// __END__

108
src/Create/Uids.php Normal file
View File

@@ -0,0 +1,108 @@
<?php
declare(strict_types=1);
namespace CoreLibs\Create;
class Uids
{
// what to use as a default hash if non ise set and no DEFAULT_HASH is defined
public const DEFAULT_HASH = 'sha256';
public const STANDARD_HASH_LONG = 'ripemd160';
public const STANDARD_HASH_SHORT = 'adler32';
/**
* creates psuedo random uuid v4
* Code take from class here:
* https://www.php.net/manual/en/function.uniqid.php#94959
*
* @return string pseudo random uuid v4
*/
public static function uuidv4(): string
{
return sprintf(
'%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
// 32 bits for "time_low"
mt_rand(0, 0xffff),
mt_rand(0, 0xffff),
// 16 bits for "time_mid"
mt_rand(0, 0xffff),
// 16 bits for "time_hi_and_version",
// four most significant bits holds version number 4
mt_rand(0, 0x0fff) | 0x4000,
// 16 bits, 8 bits for "clk_seq_hi_res",
// 8 bits for "clk_seq_low",
// two most significant bits holds zero and one for variant DCE1.1
mt_rand(0, 0x3fff) | 0x8000,
// 48 bits for "node"
mt_rand(0, 0xffff),
mt_rand(0, 0xffff),
mt_rand(0, 0xffff)
);
}
/**
* TODO: make this a proper uniq ID creation
* add uuidv4 subcall to the uuid function too
* creates a uniq id
*
* @param string $type uniq id type, currently md5 or sha256 allowed
* if not set will use DEFAULT_HASH if set
* @return string uniq id
*/
public static function uniqId(string $type = ''): string
{
$uniq_id = '';
switch ($type) {
case 'md5':
$uniq_id = md5(uniqid((string)rand(), true));
break;
case self::DEFAULT_HASH:
$uniq_id = hash(self::DEFAULT_HASH, uniqid((string)rand(), true));
break;
case self::STANDARD_HASH_LONG:
$uniq_id = hash(self::STANDARD_HASH_LONG, uniqid((string)rand(), true));
break;
case self::STANDARD_HASH_SHORT:
$uniq_id = hash(self::STANDARD_HASH_SHORT, uniqid((string)rand(), true));
break;
default:
// if not empty, check if in valid list
if (
!empty($type) &&
in_array($type, hash_algos())
) {
$hash = $type;
} else {
// fallback to default hash type if none set or invalid
$hash = self::DEFAULT_HASH;
}
$uniq_id = hash($hash, uniqid((string)rand(), true));
break;
}
return $uniq_id;
}
/**
* create a unique id with the standard hash type defined in __hash
*
* @return string Unique ID with fixed length of 8 characters
*/
public static function uniqIdShort(): string
{
return self::uniqId(self::STANDARD_HASH_SHORT);
}
/**
* create a unique id with the standard long hash type
* defined in __hashLong
*
* @return string Unique ID with length of current default long hash
*/
public static function uniqIdLong(): string
{
return self::uniqId(self::STANDARD_HASH_LONG);
}
}
// __END__