From 3ed17ac670ec9a866dcd94701bf472751b5d3d7d Mon Sep 17 00:00:00 2001 From: Kevin Burkholder <149023389+KBurkholder@users.noreply.github.com> Date: Sat, 21 Dec 2024 09:23:34 -0500 Subject: [PATCH] Doodads and Doolollys --- Doodads/cookie_consent.trait.php | 346 ++++++++++++++++++++++++++++++ Doodads/datetime.trait.php | 221 +++++++++++++++++++ Doodads/hooks.trait.php | 213 ++++++++++++++++++ Doodads/readme.md | 37 ++++ Doodads/version_compare.trait.php | 150 +++++++++++++ Doodads/zip_archive.trait.php | 90 ++++++++ 6 files changed, 1057 insertions(+) create mode 100644 Doodads/cookie_consent.trait.php create mode 100755 Doodads/datetime.trait.php create mode 100644 Doodads/hooks.trait.php create mode 100644 Doodads/readme.md create mode 100755 Doodads/version_compare.trait.php create mode 100755 Doodads/zip_archive.trait.php diff --git a/Doodads/cookie_consent.trait.php b/Doodads/cookie_consent.trait.php new file mode 100644 index 0000000..eeec62b --- /dev/null +++ b/Doodads/cookie_consent.trait.php @@ -0,0 +1,346 @@ + + * @copyright Copyright (c) 2024 EarthAsylum Consulting + * @version 24.1108.1 + * @see https://github.com/EarthAsylum/docs.eacDoojigger/wiki/How-To#wp-consent-api-and-cookies + */ +trait cookie_consent +{ + /** + * @var bool has cookie consent been loaded? + */ + private static $cookie_consent_loaded = false; + + /** + * @var string default consent service name (set by using class) + */ + public $cookie_default_service = ''; + + + /** + * initialize cookie consent (on _plugin_startup) + * + * @example $this->cookie_consent_init( $this->pluginHeader( 'PluginSlug' ) ); + * + * @param string $plugin_slug plugin/slugname (plugin_basename) + * @param string $default_service plugin or service name + * + * @return void + */ + private function cookie_consent_init( string $plugin_slug, string $default_service = '' ): void + { + if ( class_exists( '\WP_CONSENT_API' ) ) + { + $this->cookie_default_service = $default_service ?: dirname($plugin_slug); + + // WP_CONSENT_API waits for plugins_loaded to instantiate (?) + // which may be after this plugin loads. + \WP_CONSENT_API::get_instance(); + + // declare compliance with WP Consent API + if ($plugin_slug) { + add_filter( "wp_consent_api_registered_{$plugin_slug}", '__return_true' ); + } + + // do only once + if (! self::$cookie_consent_loaded) + { + self::$cookie_consent_loaded = true; + + // add 'necessary' consent category, always allowed + add_filter('wp_consent_categories', function($consent) { + return array_merge(['necessary'],$consent); + }); + // 'necessary' is always allowed + add_filter('wp_has_consent', function($has_consent, $category, $requested_by) { + return ($category == 'necessary') ? true : $has_consent; + },5,3); + + // add javascript and filter for consent type (optin/optout) + add_action( 'wp_enqueue_scripts', array( $this, 'cookie_consent_patch' ),PHP_INT_MAX); + } + } + } + + + /** + * enqueue javascripts to patch consent interface between browser and server + * + * @return void + */ + public function cookie_consent_patch(): void + { + // WP Consent API doesn't pass client consent type to the server, + // use a cookie on change (provided the CMP fires 'wp_consent_type_defined' event). + wp_add_inline_script( 'wp-consent-api', + "document.addEventListener('wp_consent_type_defined',function(){". + "consent_api_set_cookie(consent_api.cookie_prefix+'_consent_type',window.wp_consent_type);". + "});" + ); + + // some consent management platforms may not set wp_get_consent_type + add_filter('wp_get_consent_type',function($type) + { + if ((empty($type))) { // has not (yet) been set + $prefix = \WP_CONSENT_API::$config->consent_cookie_prefix(); + return $this->get_cookie("{$prefix}_consent_type",'optout'); + } + return $type; + }, + PHP_INT_MAX - 100 + ); + } + + + /** + * set a cookie supporting wp_consent if enabled + * + * @param string|array $name the cookie name + * @param mixed $value the cookie value + * @param string|int $expires cookie expiration in seconds or timestamp or string + * 'delete', 'expired', 'session', 'n days', 'n months', etc. + * @param array $options cookie parameters + * 'path', 'domain', 'secure', 'httponly', 'samesite'. + * @param mixed $consent (array) consent parameters or (string) category or true = already registered + * 'plugin_or_service', 'category', 'function', ... + * @return bool success or failure (as best we can tell) + */ + public function set_cookie(string|array $name, $value, string|int $expires=0, array $options=[], $consent=[]): bool + { + if (is_array($name)) $name = $name[0]; + + $name = sanitize_key($name); + + if ( is_array( $value ) || is_object( $value ) ) { + $value = serialize( $value ); + } else { + $value = sanitize_text_field($value); + } + + $using_consent = self::$cookie_consent_loaded; + if ($consent === true) { + $consent = ($using_consent) ? $this->get_cookie_consent($name) : []; + } + $using_consent = ($using_consent && !empty($consent)); + + list($expInt,$expStr) = $this->set_cookie_expiration($expires,$using_consent); + + $options = apply_filters( 'wp_setcookie_options', wp_parse_args( + $options, + [ + 'expires' => $expInt, + 'path' => COOKIEPATH, + 'domain' => COOKIE_DOMAIN, + 'secure' => is_ssl(), + 'httponly' => true, + 'samesite' => 'lax', + ]), + $name, + $value + ); + + unset($_COOKIE[$name]); + + if ( $using_consent ) + { + if (! $consent['category'] = wp_validate_consent_category( + apply_filters( 'wp_setcookie_category',$consent['category'],$name,$value ) + )) { + _doing_it_wrong(__FUNCTION__, __("Missing/invalid consent category."), '2.7.0'); + return false; + } + + $consent = $this->set_cookie_consent($name,$consent,true,[ + 'expires' => $expStr, + ]); + + if (! wp_has_consent($consent['category'])) return false; + } + + foreach ($options as $n => $v) + { + $options[$n] = apply_filters( "wp_setcookie_{$n}", $v, $name, $value ); + } + + if (setcookie($name,$value,$options)) + { + // echo "
".__METHOD__." ".var_export([$name,$value,$options,$consent],true)."
"; + if ($options['expires'] == 0 || $options['expires'] > time()) { + $_COOKIE[$name] = $value; + do_action( 'wp_setcookie_success',$name,$value,$options,$consent ); + } + return true; + } + + return false; + } + + + /** + * set a cookie expiration as integer and string + * + * @param string|int $expires cookie expiration in seconds or timestamp or string + * 'delete', 'expired', 'session', 'n days', 'n months', etc. + * @param bool $getString (false) return array when true + * @return array [ $expInt, $expStr ] + */ + public function set_cookie_expiration($expires, bool $getString = true): array + { + $expInt = 0; $expStr = ''; + + $utc = new \DateTimeZone('utc'); + $now = new \DateTime('now',$utc); + $time_now = $now->getTimestamp(); + + // get expires as integer + + if (is_string($expires)) { // string, i.e. '30 days' or 'session' + switch (strtolower($expires)) { + case 'session': $expInt = 0; break; + case 'delete': $expires = 'expired'; #nobreak + case 'expired': $expInt = $time_now - DAY_IN_SECONDS; break; + default: $expInt = ( new \DateTime($expires,$utc) )->getTimestamp(); + } + } else if ($expires !== 0 && $expires < $time_now) {// int seconds from/before now + $expInt = intval($time_now + $expires); + } else { // timestamp or 0 + $expInt = intval($expires); + } + + // get expires as string + + if ($getString) + { + if (is_string($expires)) { // string, i.e. '30 days' + $expStr = strtolower($expires); + } else if ($expInt == 0) { // session + $expStr = 'session'; + } else if ($expInt < $time_now) { // delete/expired + $expStr = 'expired'; + } else { // convert timestamp to days + $exp = new \DateTime('now',$utc); + $exp->setTimestamp($expInt + 60); // offset for diff() + $exp = $now->diff($exp); + foreach (['y'=>'year','m'=>'month','d'=>'day','h'=>'hour','i'=>'minute'] as $x=>$period) { + if ($x = $exp->{$x}) { + $expStr .= ($x > 1) ? "{$x} {$period}s " : "{$x} {$period} "; + } + } + $expStr = trim($expStr) ?: 'session'; + } + } + + return [$expInt,$expStr]; + } + + + /** + * set a cookie's consent information + * + * @param string $name the cookie name + * @param array|string $consent consent parameters or category + * 'plugin_or_service', 'category', 'function', ... + * @param bool $register with wp_add_cookie_info() + * @param array $defaults default consent parameters + * @return array $consent + */ + public function set_cookie_consent(string $name, $consent, bool $register = true, array $defaults = []): array + { + if (is_string($consent)) $consent = ['category' => $consent]; + $consent = apply_filters( 'wp_setcookie_consent', array_merge( + [ + 'plugin_or_service' => $this->cookie_default_service, + 'category' => '', // necessary, functional, preferences, statistics, statistics-anonymous, marketing + 'expires' => 0, + 'function' => '', // describe cookie functionality + 'collectedPersonalData' => '', // describe personal data collected + 'memberCookie' => false, + 'administratorCookie' => false, + 'type' => 'HTTP', + 'domain' => '', + ], + $defaults, + $consent + ), + $name + ); + + if ($register && !empty($consent['function']) && self::$cookie_consent_loaded) + { + // maybe replace placeholders with cookie array values + $consent['function'] = sprintf($consent['function'], + $consent['plugin_or_service'], + $consent['category'], + $consent['type'], + ); + wp_add_cookie_info( $name, ...array_values($consent) ); + } + + return $consent; + } + + + /** + * get a registered cookie consent array for a single cookie or all cookies + * + * @param string $name the cookie name (optional) + * @return array $consent or array of [name => $consent] + */ + public function get_cookie_consent($name = false): array + { + $consent = (self::$cookie_consent_loaded) ? wp_get_cookie_info($name) : false; + // because wp_get_cookie_info may return all cookies even when we ask for only one + if ($consent && $name) { + if (! isset($consent['plugin_or_service'])) return []; + } + return (is_array($consent)) ? $consent : []; + } + + + /** + * get a cookie (convenience method, since we have set_cookie) + * + * @param string|array $name the cookie name (and alternates) + * @param string $default if cookie not set + * @return string cookie value + */ + public function get_cookie(string|array $name, $default = null) + { + if (!is_array($name)) $name = array($name); + + foreach ($name as $key) { + if (isset($_COOKIE[$key])) { + $default = maybe_unserialize($_COOKIE[$key]); + break; + } + if (isset($_COOKIE[sanitize_key($key)])) { + $default = maybe_unserialize($_COOKIE[sanitize_key($key)]); + break; + } + } + + return is_string($default) ? sanitize_text_field($default) : $default; + } + + + /** + * check consent is loaded and category set (convenience method) + * + * @param string $category consent category to check. + * @return bool + */ + public function has_cookie_consent(string $category = null): bool + { + if (is_null($category)) { + return self::$cookie_consent_loaded; + } + return (self::$cookie_consent_loaded) ? wp_has_consent($category) : true; + } +} diff --git a/Doodads/datetime.trait.php b/Doodads/datetime.trait.php new file mode 100755 index 0000000..88a99b1 --- /dev/null +++ b/Doodads/datetime.trait.php @@ -0,0 +1,221 @@ + + * @copyright Copyright (c) 2021 EarthAsylum Consulting + * @version 2.x + * @link https://eacDoojigger.earthasylum.com/ + * @see https://eacDoojigger.earthasylum.com/phpdoc/ + */ + + +trait datetime +{ + /** + * Get date/time timezone + * + * @param object|string $timezone - timezone or DateTimeZone or DateTime (default = wp_timezone()) + * @param object $datetime DateTime + * @return object DateTimeZone object or false on invalid + */ + public function getTimeZone($timezone = null, $datetime = null) + { + if (empty($timezone)) + { + if ($datetime instanceOf \DateTime) { + $timezone = $datetime->getTimezone(); + } else { + $timezone = wp_timezone(); + } + } + else if (is_string($timezone)) + { + $timezone = new \DateTimeZone($timezone); + } + else if ($timezone instanceOf \DateTime) + { + $timezone = $datetime->getTimezone(); + } + + return ($timezone instanceOf \DateTimeZone) ? $timezone : false; + } + + + /** + * Get date/time + * + * @param mixed date/time string, timestamp or object + * @param string modify datetime (e.g. '+1 day') + * @param object|string timezone or DateTimeZone (default = wp_timezone()) + * @return object DateTime object or false on invalid + */ + public function getDateTime($datetime = 'now', $modify = null, $timezone = null) + { + $timezone = $this->getTimeZone($timezone,$datetime); + + if (is_numeric($datetime)) // timestamp + { + $datetime = new \DateTime('@'.$datetime); // timestamp ignores timezone here + $datetime->setTimeZone($timezone); // so set timezone here + } + else if ($datetime instanceOf \DateTime) // date/time object + { + $datetime->setTimeZone($timezone); + } + else // date/time string (Supported Date and Time Format) + { + $datetime = new \DateTime($datetime,$timezone); + } + + if (!empty($modify)) + { + $datetime->modify($modify); + } + + return $datetime; + } + + + /** + * Get date/time (returning formatted date/time string) + * + * @param string $datetime date/time string in format + * @param string $format date/time format + * @param object $timezone DateTimeZone + * @return string DateTime formatted or null on invalid + */ + public function getFormattedDateTime(string $datetime = 'now', string $format = 'Y-m-d H:i:s', $timezone = null) + { + return ($datetime = $this->getDateTime($datetime,null,$timezone)) + ? $datetime->format($format) + : null; + } + + + /** + * UTC date/time + * + * @param string date/time string + * @param int Seconds to add (subtract) to time + * @return object DateTime object or false on invalid + */ + public function getDateTimeUTC($datetime = 'now', $modify = null) + { + return $this->getDateTime($datetime, $modify, new \DateTimeZone('UTC')); + } + + + /** + * local date/time + * + * @param string date/time string + * @param int Seconds to add (subtract) to time + * @return object DateTime object or false on invalid + */ + public function getDateTimeLocal($datetime = 'now', $modify = null) + { + return $this->getDateTime($datetime, $modify, wp_timezone()); + } + + + /** + * is a valid date/time + * + * @param string $datetime date/time string in format + * @param string $format date/time format + * @param object $timezone DateTimeZone + * @return object DateTime object or false on invalid + */ + public function isValidDateTime(string $datetime, string $format = 'Y-m-d H:i:s', $timezone = null) + { + $timezone = $this->getTimeZone($timezone,$datetime); + + try { + $d = \DateTime::createFromFormat($format, $datetime, $timezone); + } catch (\Throwable $e) { + return false; + } + + return ($d && $d->format($format) == $datetime) ? $d : false; + } + + + /** + * is a valid date/time (returning formatted date/time string) + * + * @param string $datetime date/time string in format + * @param string $format date/time format + * @param object $timezone DateTimeZone + * @return string DateTime formatted or null on invalid + */ + public function isFormattedDateTime(string $datetime, string $format = 'Y-m-d H:i:s', $timezone = null) + { + return ($datetime = $this->isValidDateTime($datetime,$format,$timezone)) + ? $datetime->format($format) + : null; + } + + + /** + * is a valid date + * + * @param string $date date string in format + * @param string $format date format + * @param object $timezone DateTimeZone + * @return object DateTime object or false on invalid + */ + public function isValidDate(string $date, string $format = 'Y-m-d', $timezone = null) + { + return $this->isValidDateTime($date,$format,$timezone); + } + + + /** + * is a valid time + * + * @param string $time time string in format + * @param string $format time format + * @param object $timezone DateTimeZone + * @return object DateTime object or false on invalid + */ + public function isValidTime(string $time, string $format = 'H:i:s', $timezone = null) + { + return $this->isValidDateTime($time,$format,$timezone); + } + + + /** + * is date/time between range + * + * @param mixed $datetime date/time + * @param mixed $datetimeLo date/time low + * @param mixed $datetimeHi date/time high + * @param mixed $timezone default = wp_timezone() + * @return object DateTime object or false on invalid + */ + public function isDateTimeBetween($datetime, $datetimeLo, $datetimeHi, $timezone = null) + { + $timezone = $this->getTimeZone($timezone,$datetime); + $datetimeLo = $this->getDateTime($datetimeLo,null,$timezone); + $datetimeHi = $this->getDateTime($datetimeHi,null,$timezone); + $datetime = $this->getDateTime($datetime,null,$timezone); + + return (($datetime >= $datetimeLo) && ($datetime <= $datetimeHi)) ? $datetime : false; + } +} diff --git a/Doodads/hooks.trait.php b/Doodads/hooks.trait.php new file mode 100644 index 0000000..e82d3c5 --- /dev/null +++ b/Doodads/hooks.trait.php @@ -0,0 +1,213 @@ + + * @copyright Copyright (c) 2024 EarthAsylum Consulting + * @version 24.1212.1 + */ +trait hooks +{ + /** + * wp has_filter - returns count of filters + * + * @param string $hookName the name of the filter + * @param callable callback the callback to check for + * @return int + */ + public function wp_filter_count(string $hookName, $callback = false) + { + global $wp_filter; + $hookCount = 0; + if ( isset( $wp_filter[ $hookName ] ) ) + { + foreach ($wp_filter[ $hookName ]->callbacks as $priority) { + $hookCount += count($priority); + } + } + return $hookCount; + } + + + /** + * wp has_action - returns count of actions + * + * @param string $hookName the name of the action + * @param callable callback the callback to check for + * @return int + */ + public function wp_action_count(string $hookName, $callback = false) + { + return $this->wp_filter_count( $hookName, $callback ); + } + + + /** + * has_filter with prefixed name + * + * @param string $hookName the name of the filter + * @param callable callback the callback to check for + * @return mixed + */ + public function has_filter(string $hookName, $callback = false) + { + return \has_filter( $this->prefixHookName($hookName), $callback ); + } + + + /** + * has_filter with prefixed name - returns count of filters + * + * @param string $hookName the name of the filter + * @param callable callback the callback to check for + * @return int + */ + public function has_filter_count(string $hookName, $callback = false) + { + return $this->wp_filter_count( $this->prefixHookName($hookName), $callback ); + } + + + /** + * add_filter with prefixed name + * + * @param string $hookName the name of the filter + * @param mixed ...$args arguments passed to filter + * @return mixed + */ + public function add_filter(string $hookName,...$args) + { + return \add_filter( $this->prefixHookName($hookName), ...$args ); + } + + + /** + * remove_filter with prefixed name + * + * @param string $hookName the name of the filter + * @param mixed ...$args arguments passed to filter + * @return mixed + */ + public function remove_filter(string $hookName,...$args) + { + return \remove_filter( $this->prefixHookName($hookName), ...$args ); + } + + + /** + * apply_filters with prefixed name + * + * @param string $hookName the name of the filter + * @param mixed ...$args arguments passed to filter + * @return mixed + */ + public function apply_filters(string $hookName,...$args) + { + return \apply_filters( $this->prefixHookName($hookName), ...$args ); + } + + + /** + * did_filter with prefixed name (WP 6.1) + * + * @param string $hookName the name of the filter + * @return mixed + */ + public function did_filter(string $hookName) + { + return \did_filter( $this->prefixHookName($hookName) ); + } + + + /** + * has_action with prefixed name + * + * @param string $hookName the name of the action + * @param callable callback the callback to check for + * @return mixed + */ + public function has_action(string $hookName, $callback = false) + { + return \has_action( $this->prefixHookName($hookName), $callback ); + } + + + /** + * has_action with prefixed name - returns count of filters + * + * @param string $hookName the name of the action + * @param callable callback the callback to check for + * @return int + */ + public function has_action_count(string $hookName, $callback = false) + { + return $this->wp_action_count( $this->prefixHookName($hookName), $callback ); + } + + + /** + * add_action with prefixed name + * + * @param string $hookName the name of the action + * @param mixed ...$args arguments passed to action + * @return mixed + */ + public function add_action(string $hookName,...$args) + { + return \add_action( $this->prefixHookName($hookName), ...$args ); + } + + + /** + * remove_action with prefixed name + * + * @param string $hookName the name of the action + * @param mixed ...$args arguments passed to action + * @return mixed + */ + public function remove_action(string $hookName,...$args) + { + return \remove_action( $this->prefixHookName($hookName), ...$args ); + } + + + /** + * do_action with prefixed name + * + * @param string $hookName the name of the action + * @param mixed ...$args arguments passed to action + * @return mixed + */ + public function do_action(string $hookName,...$args) + { + return \do_action( $this->prefixHookName($hookName), ...$args ); + } + + + /** + * did_action with prefixed name + * + * @param string $hookName the name of the action + * @return mixed + */ + public function did_action(string $hookName) + { + return \did_action( $this->prefixHookName($hookName) ); + } + + + /** + * Get the prefixed version of the hook name + * + * @param string $hookName filter/action name + * @return string hookname with prefix + */ + public function prefixHookName(string $hookName): string + { + return $this->addClassNamePrefix($hookName); + } +} diff --git a/Doodads/readme.md b/Doodads/readme.md new file mode 100644 index 0000000..17bf6b6 --- /dev/null +++ b/Doodads/readme.md @@ -0,0 +1,37 @@ +## {eac}Doojigger, Plugin Derivatives +[![EarthAsylum Consulting](https://img.shields.io/badge/EarthAsylum-Consulting-0?&labelColor=6e9882&color=707070)](https://earthasylum.com/) +[![WordPress](https://img.shields.io/badge/WordPress-Plugins-grey?logo=wordpress&labelColor=blue)](https://wordpress.org/plugins/search/EarthAsylum/) +[![eacDoojigger](https://img.shields.io/badge/Requires-%7Beac%7DDoojigger-da821d)](https://eacDoojigger.earthasylum.com/) + +
Document Header + +Plugin URI: https://eacDoojigger.earthasylum.com/ +Author: [EarthAsylum Consulting](https://www.earthasylum.com) +Last Updated: 21-Dec-2024 +Contributors: [earthasylum](https://github.com/earthasylum),[kevinburkholder](https://profiles.wordpress.org/kevinburkholder) +Requires EAC: 3.0 +WordPress URI: https://wordpress.org/plugins/search/earthasylum/ +GitHub URI: https://github.com/EarthAsylum/docs.eacDoojigger/wiki/ + +
+ +_doodad_ (n) +1. Something, especially a small device or part, whose name is unknown or forgotten. +2. *A helper or trait included with a Doojigger plugin.* + +### Description + +The 'Doodads' included here are code snippets, helpers, or traits that are a part of the [{eac}Doojigger](https://eacdoojigger.earthasylum.com) plugin for WordPress. + +As such, these 'Doodads' may not work 'as-is' outside of the {eac}Doojigger plugin but do provide examples and methods that may be used or adapted to your project. + +Details on some of these 'Doodads' may be found [here](https://github.com/EarthAsylum/docs.eacDoojigger/wiki/How-To). + + +#### For further documentation, see: + +:open_file_folder: [{eac}Doojigger Wiki: documentation and examples](https://github.com/EarthAsylum/docs.eacDoojigger/wiki) + +:bookmark_tabs: [{eac}Doojigger Web Site](https://eacdoojigger.earthasylum.com) + +:green_book: [{eac}Doojigger PHP Reference](https://earthasylum.github.io/docs.eacDoojigger/) diff --git a/Doodads/version_compare.trait.php b/Doodads/version_compare.trait.php new file mode 100755 index 0000000..52faad0 --- /dev/null +++ b/Doodads/version_compare.trait.php @@ -0,0 +1,150 @@ + + * @copyright Copyright (c) 2021 EarthAsylum Consulting + * @version 2.x + * @link https://eacDoojigger.earthasylum.com/ + * @see https://eacDoojigger.earthasylum.com/phpdoc/ + */ + +trait version_compare +{ + /** + * is version compatable (>=) + * + * @param string $version (n.n.n) + * @param string $required (n.n.n) + * @return bool true if $version >= $required + */ + public function isVersionCompatable(string $version, string $required): bool + { + switch(strtolower($required)) + { + case 'php': + return \is_php_version_compatible($version); + case 'wordpress': + case 'wp': + return \is_wp_version_compatible($version); + case 'woocommerce': + case 'wc': + return (defined('WC_VERSION') && version_compare(WC_VERSION, $version, 'ge')) ? true : false; + case 'eacdoojigger': + case '{eac}doojigger': + case 'eac': + return (defined('EACDOOJIGGER_VERSION') && version_compare(EACDOOJIGGER_VERSION, $version, 'ge')) ? true : false; + } + return version_compare($version, $required, 'ge'); + } + + + /** + * version compare + * + * @param string $version1 (n.n.n) + * @param string $version2 (n.n.n) + * @param mixed $eqVal returned if $versions1 = $version2 (true) + * @param mixed $ltVal returned if $versions1 < $version2 (-1) + * @param mixed $gtVal returned if $versions1 > $version2 (1) + * @return bool|int true if $versions1 = $version2, $ltVal if $versions1 < $version2, $gtVal if $versions1 > $version2 + */ + public function isVersionCompare(string $version1, string $version2, $eqVal = true, $ltVal = -1, $gtVal = +1) + { + switch (version_compare($version1, $version2)) { + case -1 : return $ltVal; + case 0 : return $eqVal; + case 1 : return $gtVal; + } + } + + + /** + * version compare equal to + * + * @param string $version1 (n.n.n) + * @param string $version2 (n.n.n) + * @return bool true if $versions1 = $version2 + */ + public function isVersionEqualTo(string $version1, string $version2): bool + { + return version_compare($version1, $version2, 'eq'); + } + + + /** + * version compare not equal to + * + * @param string $version1 (n.n.n) + * @param string $version2 (n.n.n) + * @return bool true if $versions1 <> $version2 + */ + public function isVersionNotEqualTo(string $version1, string $version2): bool + { + return version_compare($version1, $version2, 'ne'); + } + + + /** + * version compare less than + * + * @param string $version1 (n.n.n) + * @param string $version2 (n.n.n) + * @return bool true if $versions1 < $version2 + */ + public function isVersionLessThan(string $version1, string $version2): bool + { + return version_compare($version1, $version2, 'lt'); + } + + + /** + * version compare less than or equal to + * + * @param string $version1 (n.n.n) + * @param string $version2 (n.n.n) + * @return bool true if $versions1 <= $version2 + */ + public function isVersionLessThanEqualTo(string $version1, string $version2): bool + { + return version_compare($version1, $version2, 'le'); + } + + + /** + * version compare greater than + * + * @param string $version1 (n.n.n) + * @param string $version2 (n.n.n) + * @return bool true if $versions1 > $version2 + */ + public function isVersionGreaterThan(string $version1, string $version2): bool + { + return version_compare($version1, $version2, 'gt'); + } + + + /** + * version compare greater than or equal to + * + * @param string $version1 (n.n.n) + * @param string $version2 (n.n.n) + * @return bool true if $versions1 >= $version2 + */ + public function isVersionGreaterThanEqualTo(string $version1, string $version2): bool + { + return version_compare($version1, $version2, 'ge'); + } +} diff --git a/Doodads/zip_archive.trait.php b/Doodads/zip_archive.trait.php new file mode 100755 index 0000000..1285fbc --- /dev/null +++ b/Doodads/zip_archive.trait.php @@ -0,0 +1,90 @@ + + * @copyright Copyright (c) 2021 EarthAsylum Consulting + * @version 2.x + * @link https://eacDoojigger.earthasylum.com/ + * @see https://eacDoojigger.earthasylum.com/phpdoc/ + */ + +trait zip_archive +{ + /** + * Create a new ZipArchive + * + * @param string folder to archive + * @param string file name of new zip (_.zip) + * @param bool delete files after archiving + * @return object|bool zip archive or false + */ + public function zip_archive_create(string $pathName, string $fileName, bool $purge = false) + { + if ( ! class_exists( '\ZipArchive' ) ) + { + return false; + } + + // Get real path for our folder + $rootPath = realpath($pathName); + + // Initialize archive object + $zip = new \ZipArchive(); + $zip->open($fileName, \ZipArchive::CREATE | \ZipArchive::OVERWRITE); + + // Initialize empty "delete list" + $filesToDelete = array(); + + // Create recursive directory iterator + $files = new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($rootPath), + \RecursiveIteratorIterator::LEAVES_ONLY + ); + + foreach ($files as $name => $file) + { + // Skip directories (they would be added automatically) + if (!$file->isDir()) + { + // Get real and relative path for current file + $filePath = $file->getRealPath(); + $relativePath = substr($filePath, strlen($rootPath) + 1); + + // Add current file to archive + $zip->addFile($filePath, $relativePath); + + // Add current file to "delete list" + $filesToDelete[] = $filePath; + } + } + + // Zip archive will be created only after closing object + $zip->close(); + + if ($purge === true) + { + // Delete all files from "delete list" + foreach ($filesToDelete as $file) + { + unlink($file); + } + } + + return $zip; + } +}