Your IP : 216.73.216.240


Current Path : /home/juvelize/saulnois/libraries/ic_library/Vendor/Icalcreator/src/
Upload File :
Current File : /home/juvelize/saulnois/libraries/ic_library/Vendor/Icalcreator/src/Pc.php

<?php
/**
 * iCalcreator, the PHP class package managing iCal (rfc2445/rfc5445) calendar information.
 *
 * This file is a part of iCalcreator.
 *
 * @author    Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
 * @copyright 2007-2024 Kjell-Inge Gustafsson, kigkonsult AB, All rights reserved
 * @link      https://kigkonsult.se
 * @license   Subject matter of licence is the software iCalcreator.
 *            The above copyright, link, package and version notices,
 *            this licence notice and the invariant [rfc5545] PRODID result use
 *            as implemented and invoked in iCalcreator shall be included in
 *            all copies or substantial portions of the iCalcreator.
 *
 *            iCalcreator is free software: you can redistribute it and/or modify
 *            it under the terms of the GNU Lesser General Public License as
 *            published by the Free Software Foundation, either version 3 of
 *            the License, or (at your option) any later version.
 *
 *            iCalcreator is distributed in the hope that it will be useful,
 *            but WITHOUT ANY WARRANTY; without even the implied warranty of
 *            MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *            GNU Lesser General Public License for more details.
 *
 *            You should have received a copy of the GNU Lesser General Public License
 *            along with iCalcreator. If not, see <https://www.gnu.org/licenses/>.
 */
declare( strict_types = 1 );
namespace Kigkonsult\Icalcreator;

use ArrayObject;

use DateTimeInterface;
use Kigkonsult\Icalcreator\Util\StringFactory;

use function array_keys;
use function in_array;
use function is_array;
use function is_bool;
use function is_int;
use function is_scalar;
use function is_string;
use function str_contains;
use function str_replace;
use function str_starts_with;
use function strtoupper;
use function substr_count;
use function trim;
use function var_export;

/**
 * Property Contents
 *
 * @since 2.41.89 2024-01-20
 */
class Pc extends ArrayObject
{
    /**
     * @var string  Component property value, (array) key
     */
    public static string $LCvalue       = 'value';

    /**
     * @var string  Component property parameters, (array) key
     */
    public static string $LCparams      = 'params';

    /**
     * @var mixed[]
     */
    private static array $propTmpl      = [ 'value' => null, 'params' => [] ];

    /**
     * @var string
     */
    private static string $iteratorTmpl = 'ArrayIterator';

    /**
     * The property value
     *
     * @var mixed
     */
    public mixed $value;

    /**
     * The property parameters
     *
     * @var null|int|mixed[]
     */
    public null|int|array $params;

    /**
     * Class constructor
     *
     * @overrides
     * @param null|mixed[] $array           ignored
     * @param null|int $flags             ignored
     * @param null|string $iteratorClass  ArrayIterator
     */
    public function __construct(
        ? array $array = [],
        ? int $flags = 0,
        ? string $iteratorClass = 'ArrayIterator'
    )
    {
        parent::__construct(
            self::$propTmpl,
            ArrayObject::ARRAY_AS_PROPS,
            self::$iteratorTmpl
        );
    }

    /**
     * Class factory method
     *
     * @param mixed|null $value      ant property value or a Pc
     * @param null|string[] $params
     * @return Pc
     */
    public static function factory( mixed $value = null, ? array $params = [] ) : Pc
    {
        if( $value instanceof Pc ) {
            return clone $value;
        }
        $instance = new self();
        if( null !== $value ) {
            $instance->setValue( $value );
        }
        if( ! empty( $params )) {
            $instance->setParams( $params );
        }
        return $instance;
    }

    /**
     * Inherited but 'closed' methods
     */
    public function exchangeArray( mixed $array ) : array { return $this->getArrayCopy(); }
    public function setFlags( mixed $flags ) : void {}
    public function setIteratorClass( mixed $iteratorClass ) : void {}

    /**
     * Value methods
     */

    /**
     * @return mixed[]
     */
    public function getAsArray() : array
    {
        return $this->getArrayCopy();
    }

    /**
     * Return bool true if property value is set, i.e not null
     *
     * @return bool
     */
    public function isset() : bool
    {
        return ( null !== $this->value );
    }

    /**
     * Return value, false if not set
     *
     * @return mixed
     */
    public function getValue() : mixed
    {
        return $this->value ?? false;
    }

    /**
     * Set to 'empty' state
     *
     * @return Pc
     */
    public function setEmpty() : Pc
    {
        static $SP0   = '';
        $this->value  = $SP0;
        $this->params = [];
        return $this;
    }

    /**
     * Set value
     *
     * @param mixed|null $value
     * @return Pc
     */
    public function setValue( mixed $value = null ) : Pc
    {
        $this->value = $value;
        return $this;
    }

    /**
     * Parameters methods
     */

    /**
     * Return all parameters (array) or single parameter key value, null if not exists
     *
     * @param null|string $pKey   parameter key
     * @param bool $asXparamKey   only if not empty pKey, opt do X-prefix pkey
     * @return null|int|string|string[]
     * @since 2.41.88 - 2024-01-18
     */
    public function getParams( ? string $pKey = null, ? bool $asXparamKey = false ) : null|int|string|array
    {
        if( null === $pKey ) {
            return $this->params;
        }
        $pKey = $asXparamKey ? self::setXPrefix( $pKey ) : strtoupper( $pKey );
        return $this->hasParamKey( $pKey ) ? $this->params[$pKey] : null;
    }

    /**
     * Return value of the parameter key VALUE or null
     *
     * @return string|null
     */
    public function getValueParam() : null|string
    {
        return $this->params[IcalInterface::VALUE] ?? null;
    }

    /**
     * Return bool true if params has key, opt with spec. value
     *
     * @param string $pKey
     * @param null|string $pValue
     * @return bool
     */
    public function hasParamKey( string $pKey, ? string $pValue = null ) : bool
    {
        $key = strtoupper( $pKey);
        return ( null === $pValue )
            ? isset( $this->params[$key] )
            : ( isset( $this->params[$key] ) && ( $pValue === $this->params[$key] ));
    }

    /**
     * Return bool true if params has ISLOCALTIME key
     *
     * @return bool
     */
    public function hasParamIsLocalTime() : bool
    {
        return $this->hasParamKey( IcalInterface::ISLOCALTIME );
    }

    /**
     * Return bool true if params has VALUE key, opt with spec. value
     *
     * @param null|string $pValue
     * @return bool
     */
    public function hasParamValue( ? string $pValue = null ) : bool
    {
        return $this->hasParamKey( IcalInterface::VALUE, $pValue );
    }

    /**
     * Return bool true if params has X-key, opt with spec. value
     *
     * @param string $pKey
     * @param null|string $pValue
     * @return bool
     */
    public function hasXparamKey( string $pKey, ? string $pValue = null ) : bool
    {
        return $this->hasParamKey( self::setXPrefix( $pKey), $pValue );
    }

    /**
     * Return array, parameter keys
     *
     * @return string[]
     */
    public function getParamKeys() : array
    {
        return array_keys( $this->params );
    }

    /**
     * Remove parameter key, opt only with spec. value
     *
     * @param null|string $pKey
     * @param null|string $pValue
     * @return Pc
     */
    public function removeParam( ? string $pKey = null, ? string $pValue = null  ) : Pc
    {
        if(( null === $pKey ) && ( null === $pValue )) {
            $this->params = [];
        }
        elseif(( null === $pValue ) || ( $this->hasParamKey( $pKey, $pValue ))) {
            unset( $this->params[strtoupper( $pKey )] );
        }
        return $this;
    }

    /**
     * Remove parameter X-key
     *
     * @param string $pKey
     * @return Pc
     */
    public function removeXparam( string $pKey ) : Pc
    {
        return $this->removeParam( self::setXPrefix( $pKey ));
    }

    /**
     * Set parameter key/value, default overwrite (key always set to uppercase)
     *
     * @param string $pKey
     * @param mixed $pValue
     * @param null|bool $overwrite
     * @return Pc
     * @since 2.41.89 2024-01-20
     */
    public function addParam( string $pKey, mixed $pValue, ? bool $overwrite = true ) : Pc
    {
        self::conformParamKeyValue( $pKey, $pValue );
        if( $overwrite || ! $this->hasParamKey( $pKey )) {
            $this->params[$pKey] = $pValue;
        }
        return $this;
    }

    /**
     * Set value for parameter key VALUE, default overwrite (key always set to uppercase)
     *
     * @param mixed $pValue
     * @param null|bool $overwrite
     * @return Pc
     */
    public function addParamValue( mixed $pValue, ? bool $overwrite = true ) : Pc
    {
        return $this->addParam( IcalInterface::VALUE, $pValue, $overwrite );
    }

    /**
     * Set parameter X-key/value, overwrite if exists (key always set to uppercase, x-prefixed if missing)
     *
     * @param string $pKey
     * @param mixed $pValue
     * @param null|bool $overwrite
     * @return Pc
     */
    public function addXparam( string $pKey, mixed $pValue, ? bool $overwrite = true  ) : Pc
    {
        return $this->addParam( self::setXPrefix( $pKey ), $pValue, $overwrite );
    }

    /**
     * Set parameter X-key/value array, default overwrite (key always set to uppercase )
     *
     * Conform parameters first
     *
     * @param string[] $params
     * @param null|bool $overwrite
     * @return Pc
     * @since 2.41.85 2024-01-19
     */
    public function setParams( array $params, ? bool $overwrite = true  ) : Pc
    {
        foreach( $params as $kPkey => $pValue ) {
            $this->addParam( $kPkey, $pValue, $overwrite );
        }
        return $this;
    }

    /**
     * @return string
     * @since 2.41.85 2024-01-16
     */
    public function __toString() : string
    {
        static $FORMAT = 'Ymd-His e';
        static $SP0    = '';
        static $GTEQ   = '=>';
        static $CMSP1  = ', ';
        $str = match ( true ) {
            null === $this->value     => $SP0,
            is_scalar( $this->value ) => (string) $this->value,
            $this->value instanceof DateTimeInterface => $this->value->format( $FORMAT ),
            default                   => str_replace( PHP_EOL, $SP0, var_export( $this->value, true  )),
        }; // end match
        if( is_array( $this->params )) {
            foreach( $this->params as $k => $v ) {
                $str .= $CMSP1 . $k . $GTEQ . $v;
            }
        }
        return rtrim( $str );
    }

    /**
     * Parameters static key methods
     */

    /**
     * @var string
     */
    protected static string $xPrefix = 'X-';

    /**
     * Return bool true if (key-)value is X-prefixed
     *
     * @param string $key
     * @return bool
     */
    public static function isXprefixed( string $key ) : bool
    {
        return str_starts_with( strtoupper( $key ), self::$xPrefix );
    }

    /**
     * Return X-prefixed key in upper case
     *
     * @param string $key
     * @return string
     */
    public static function setXPrefix( string $key ) : string
    {
        return strtoupper( self::isXprefixed( $key ) ? $key : self::$xPrefix . $key );
    }

    /**
     * Return string with opt. leading x-prefix removed
     *
     * @param string $key
     * @return string
     */
    public static function unsetXPrefix( string $key ) : string
    {
        return self::isXprefixed( $key ) ? substr( $key, 2 ) : $key;
    }

    /**
     * Parameters static value methods
     */

    private static string $CIRCUMFLEX = '^';
    private static string $CFN        = '^n';
    private static string $CFCF       = '^^';
    private static string $CFSQ       = "^'";
    private static string $CFQQ       = '^"';
    private static string $NLCHARS    = '\n';

    /**
     * Return parsed parameter VALUE with opt. circumflex deformatted as of rfc6868
     *
     * the character sequence ^n (U+005E, U+006E) is decoded into an
     *    appropriate formatted line break according to the type of system
     *    being used
     * the character sequence ^^ (U+005E, U+005E) is decoded into the ^ character (U+005E)
     * the character sequence ^' (U+005E, U+0027) is decoded into the " character (U+0022)
     * if a ^ (U+005E) character is followed by any character other than the ones above,
     *    parsers MUST leave both the ^ and the following character in place
     *
     * Also ^" and ' are decoded into the " character (U+0022), NOT rfc6868
     *
     * @param mixed $value
     * @return mixed
     * @since 2.41.89 2024-01-20
     */
    public static function circumflexQuoteParse( mixed $value ) : mixed
    {
        static $SQUOTE = "'";
        if( ! is_string( $value )) {
            return $value;
        }
        if( str_contains( $value, self::$CFN )) {
            $value = str_replace( self::$CFN, self::$NLCHARS, $value );
        }
        if( str_contains( $value, self::$CFCF )) {
            $value = str_replace( self::$CFCF, self::$CIRCUMFLEX, $value );
        }
        if( str_contains( $value, self::$CFSQ )) {
            $value = str_replace( self::$CFSQ, StringFactory::$QQ, $value );
        }
        if( str_contains( $value, self::$CFQQ )) {
            $value = str_replace( self::$CFQQ, StringFactory::$QQ, $value );
        }
        if( str_contains( $value, $SQUOTE ) && ( 0 === ( substr_count( $value, $SQUOTE ) % 2 ))) {
            $value = str_replace( $SQUOTE, StringFactory::$QQ, $value );
        }
        return $value;
    }
    /**
     * Return (conformed) iCal component property parameters
     *
     * Trim quoted values, default parameters may be set, if missing
     * Non-string values set to string
     *
     * @param string $pKey
     * @param mixed  $pValue
     * @return void
     * @since 2.41.89 2024-01-20
     */
    public static function conformParamKeyValue( string & $pKey, mixed & $pValue ) : void
    {
        static $TRUEFALSEARR = [ IcalInterface::TRUE,  IcalInterface::FALSE ];
        $pKey   = strtoupper( $pKey );
        $pValue = self::circumflexQuoteParse( $pValue );
        switch( $pKey ) {
            case IcalInterface::ISLOCALTIME :
                break;
            case IcalInterface::DERIVED :
                if( is_bool( $pValue )) {
                    $pValue = $pValue ? IcalInterface::TRUE : IcalInterface::FALSE;
                }
                elseif( in_array( strtoupper( $pValue ), $TRUEFALSEARR, true )) {
                    $pValue = strtoupper( $pValue );
                }
                break;
            case IcalInterface::ORDER :
                if( ! is_int( $pValue )) {
                    $pValue = (int) $pValue;
                }
                if( 1 > $pValue ) {
                    $pValue = 1;
                }
                break;
            case IcalInterface::VALUE :
                $pValue = strtoupper( $pValue );
                break;
            default :
                if( is_array( $pValue )) {
                    foreach( $pValue as $pkey2 => $pValue2 ) {
                        $pValue[$pkey2] = self::conformValue( $pValue2 );
                    }
                    break;
                }
                $pValue = self::conformValue( $pValue );
                break;
        } // end switch
    }

    /**
     * @param mixed $value
     * @return string
     * @since 2.41.89 2024-01-20
     */
    private static function conformValue ( mixed $value ) : string
    {
        static $ONE = '1';
        return match ( true ) {
            is_string( $value ) => trim( $value, StringFactory::$QQ ),
            is_bool( $value )   => $value ? $ONE : StringFactory::$ZERO,
            default             => (string) $value,
        }; // end match
    }
}