Version Description
Version 2.0 is a significant rewrite. The plugin now requires PHP 5.6, just like WordPress 5.2 does.
The GitHub and Composer repository was moved from tillkruss/redis-cache
to rhubarbgroup/redis-cache
.
- Require PHP 5.6
- Plugin is now "network-only"
- Switch to WPCS for code standards
- Overhauled the settings screen
- Added object cache metrics (on dashboard widget and settings)
- Added support for Query Monitor
- Added
Rhubarb\RedisCache
namespace to all files - Added support for WP 5.5's new
wp_cache_get_multi()
function - Added
redis_object_cache()
function to retrieve plugin instance - Added dropin warnings to network dashboard
- Added support for setting Sentinel database numbers
- Support Redis 6 ACL username and password authentication
- Support overwriting existing dropin on setting screen
- Use singleton pattern to instantiate plugin
- Use Composer to install and load Predis
- Update object cache dropin during plugin update
- Use separate methods to connect with all clients
- Removed
CUSTOM_USER_TABLE
andCUSTOM_USER_META_TABLE
weirdness - Added
themes
as ignored group - Changed default connection and read timeout to 1 second
- Prevent race condition in
add_or_replace()
- Renamed
WP_CACHE_KEY_SALT
toWP_REDIS_PREFIX
for clarity - Replaced "slave" terminology with "replica"
- Only
SELECT
database when it's not0
Download this release
Release Info
Developer | tillkruess |
Plugin | Redis Object Cache |
Version | 2.0.0 |
Comparing to | |
See all releases |
Code changes from version 1.6.3 to 2.0.0
- assets/css/admin.css +160 -0
- assets/js/admin.js +461 -0
- assets/js/apexcharts.min.js +14 -0
- dependencies/vendor/autoload.php +7 -0
- dependencies/vendor/colinmollenhour/credis/Client.php +1490 -0
- dependencies/vendor/colinmollenhour/credis/Cluster.php +343 -0
- dependencies/vendor/colinmollenhour/credis/LICENSE +23 -0
- dependencies/vendor/colinmollenhour/credis/Module.php +68 -0
- dependencies/vendor/colinmollenhour/credis/README.markdown +206 -0
- dependencies/vendor/colinmollenhour/credis/Sentinel.php +360 -0
- dependencies/vendor/colinmollenhour/credis/phpunit_local.sh +26 -0
- dependencies/vendor/colinmollenhour/credis/testenv/env/php-5.5/Dockerfile +24 -0
- dependencies/vendor/colinmollenhour/credis/testenv/env/php-5.6/Dockerfile +24 -0
- dependencies/vendor/colinmollenhour/credis/testenv/env/php-7.0/Dockerfile +24 -0
- dependencies/vendor/colinmollenhour/credis/testenv/env/php-7.1/Dockerfile +24 -0
- dependencies/vendor/colinmollenhour/credis/testenv/env/php-7.2/Dockerfile +24 -0
- dependencies/vendor/colinmollenhour/credis/testenv/env/php-7.3/Dockerfile +24 -0
- dependencies/vendor/colinmollenhour/credis/tests/CredisClusterTest.php +211 -0
- dependencies/vendor/colinmollenhour/credis/tests/CredisSentinelTest.php +201 -0
- dependencies/vendor/colinmollenhour/credis/tests/CredisStandaloneClusterTest.php +30 -0
- dependencies/vendor/colinmollenhour/credis/tests/CredisStandaloneSentinelTest.php +8 -0
- dependencies/vendor/colinmollenhour/credis/tests/CredisStandaloneTest.php +44 -0
- dependencies/vendor/colinmollenhour/credis/tests/CredisTest.php +793 -0
- dependencies/vendor/colinmollenhour/credis/tests/CredisTestCommon.php +187 -0
- dependencies/vendor/colinmollenhour/credis/tests/keys.test +100 -0
- dependencies/vendor/colinmollenhour/credis/tests/redis-2.conf +5 -0
- dependencies/vendor/colinmollenhour/credis/tests/redis-3.conf +5 -0
- dependencies/vendor/colinmollenhour/credis/tests/redis-4.conf +5 -0
- dependencies/vendor/colinmollenhour/credis/tests/redis-auth.conf +6 -0
- dependencies/vendor/colinmollenhour/credis/tests/redis-master.conf +8 -0
- dependencies/vendor/colinmollenhour/credis/tests/redis-sentinel.conf +6 -0
- dependencies/vendor/colinmollenhour/credis/tests/redis-slave.conf +7 -0
- dependencies/vendor/colinmollenhour/credis/tests/redis-socket.conf +6 -0
- dependencies/vendor/colinmollenhour/credis/tests/redis_config.json +9 -0
- dependencies/vendor/colinmollenhour/credis/tests/sentinel_config.json +1 -0
- dependencies/vendor/composer/ClassLoader.php +445 -0
- dependencies/vendor/composer/LICENSE +56 -0
- dependencies/vendor/composer/autoload_classmap.php +14 -0
- dependencies/vendor/composer/autoload_namespaces.php +9 -0
- dependencies/vendor/composer/autoload_psr4.php +10 -0
- dependencies/vendor/composer/autoload_real.php +52 -0
- dependencies/vendor/composer/autoload_static.php +40 -0
- dependencies/vendor/composer/installed.json +96 -0
- dependencies/vendor/predis/predis/CHANGELOG.md +985 -0
- dependencies/vendor/predis/predis/CONTRIBUTING.md +44 -0
- dependencies/vendor/predis/predis/FAQ.md +177 -0
- {includes → dependencies/vendor/predis}/predis/LICENSE +0 -0
- dependencies/vendor/predis/predis/README.md +492 -0
- {includes → dependencies/vendor/predis}/predis/VERSION +0 -0
- {includes → dependencies/vendor/predis}/predis/autoload.php +0 -0
- dependencies/vendor/predis/predis/bin/create-command-test +275 -0
- dependencies/vendor/predis/predis/bin/create-pear +233 -0
- dependencies/vendor/predis/predis/bin/create-phar +71 -0
- dependencies/vendor/predis/predis/bin/create-single-file +662 -0
- dependencies/vendor/predis/predis/examples/custom_cluster_distributor.php +117 -0
- dependencies/vendor/predis/predis/examples/debuggable_connection.php +92 -0
- dependencies/vendor/predis/predis/examples/dispatcher_loop.php +79 -0
- dependencies/vendor/predis/predis/examples/executing_redis_commands.php +57 -0
- dependencies/vendor/predis/predis/examples/key_prefixing.php +36 -0
- dependencies/vendor/predis/predis/examples/lua_scripting_abstraction.php +71 -0
- dependencies/vendor/predis/predis/examples/monitor_consumer.php +44 -0
- dependencies/vendor/predis/predis/examples/pipelining_commands.php +45 -0
- dependencies/vendor/predis/predis/examples/pubsub_consumer.php +59 -0
- dependencies/vendor/predis/predis/examples/redis_collections_iterators.php +99 -0
- dependencies/vendor/predis/predis/examples/replication_complex.php +85 -0
- dependencies/vendor/predis/predis/examples/replication_sentinel.php +58 -0
- dependencies/vendor/predis/predis/examples/replication_simple.php +52 -0
- dependencies/vendor/predis/predis/examples/session_handler.php +52 -0
- dependencies/vendor/predis/predis/examples/shared.php +44 -0
- dependencies/vendor/predis/predis/examples/transaction_using_cas.php +52 -0
- dependencies/vendor/predis/predis/package.ini +36 -0
- {includes → dependencies/vendor/predis}/predis/src/Autoloader.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Client.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/ClientContextInterface.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/ClientException.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/ClientInterface.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Cluster/ClusterStrategy.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Cluster/Distributor/DistributorInterface.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Cluster/Distributor/EmptyRingException.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Cluster/Distributor/HashRing.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Cluster/Distributor/KetamaRing.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Cluster/Hash/CRC16.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Cluster/Hash/HashGeneratorInterface.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Cluster/PredisStrategy.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Cluster/RedisStrategy.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Cluster/StrategyInterface.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Collection/Iterator/CursorBasedIterator.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Collection/Iterator/HashKey.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Collection/Iterator/Keyspace.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Collection/Iterator/ListKey.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Collection/Iterator/SetKey.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Collection/Iterator/SortedSetKey.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/Command.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/CommandInterface.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ConnectionAuth.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ConnectionEcho.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ConnectionPing.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ConnectionQuit.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ConnectionSelect.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/GeospatialGeoAdd.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/GeospatialGeoDist.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/GeospatialGeoHash.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/GeospatialGeoPos.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/GeospatialGeoRadius.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/GeospatialGeoRadiusByMember.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/HashDelete.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/HashExists.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/HashGet.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/HashGetAll.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/HashGetMultiple.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/HashIncrementBy.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/HashIncrementByFloat.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/HashKeys.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/HashLength.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/HashScan.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/HashSet.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/HashSetMultiple.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/HashSetPreserve.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/HashStringLength.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/HashValues.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/HyperLogLogAdd.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/HyperLogLogCount.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/HyperLogLogMerge.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/KeyDelete.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/KeyDump.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/KeyExists.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/KeyExpire.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/KeyExpireAt.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/KeyKeys.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/KeyMigrate.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/KeyMove.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/KeyPersist.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/KeyPreciseExpire.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/KeyPreciseExpireAt.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/KeyPreciseTimeToLive.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/KeyRandom.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/KeyRename.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/KeyRenamePreserve.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/KeyRestore.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/KeyScan.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/KeySort.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/KeyTimeToLive.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/KeyType.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ListIndex.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ListInsert.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ListLength.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ListPopFirst.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ListPopFirstBlocking.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ListPopLast.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ListPopLastBlocking.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ListPopLastPushHead.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ListPopLastPushHeadBlocking.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ListPushHead.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ListPushHeadX.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ListPushTail.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ListPushTailX.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ListRange.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ListRemove.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ListSet.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ListTrim.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/PrefixableCommandInterface.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/Processor/KeyPrefixProcessor.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/Processor/ProcessorChain.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/Processor/ProcessorInterface.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/PubSubPublish.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/PubSubPubsub.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/PubSubSubscribe.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/PubSubSubscribeByPattern.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/PubSubUnsubscribe.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/PubSubUnsubscribeByPattern.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/RawCommand.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ScriptCommand.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ServerBackgroundRewriteAOF.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ServerBackgroundSave.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ServerClient.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ServerCommand.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ServerConfig.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ServerDatabaseSize.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ServerEval.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ServerEvalSHA.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ServerFlushAll.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ServerFlushDatabase.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ServerInfo.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ServerInfoV26x.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ServerLastSave.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ServerMonitor.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ServerObject.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ServerSave.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ServerScript.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ServerSentinel.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ServerShutdown.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ServerSlaveOf.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ServerSlowlog.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ServerTime.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/SetAdd.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/SetCardinality.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/SetDifference.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/SetDifferenceStore.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/SetIntersection.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/SetIntersectionStore.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/SetIsMember.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/SetMembers.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/SetMove.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/SetPop.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/SetRandomMember.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/SetRemove.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/SetScan.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/SetUnion.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/SetUnionStore.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/StringAppend.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/StringBitCount.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/StringBitField.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/StringBitOp.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/StringBitPos.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/StringDecrement.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/StringDecrementBy.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/StringGet.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/StringGetBit.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/StringGetMultiple.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/StringGetRange.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/StringGetSet.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/StringIncrement.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/StringIncrementBy.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/StringIncrementByFloat.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/StringPreciseSetExpire.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/StringSet.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/StringSetBit.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/StringSetExpire.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/StringSetMultiple.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/StringSetMultiplePreserve.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/StringSetPreserve.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/StringSetRange.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/StringStrlen.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/StringSubstr.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/TransactionDiscard.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/TransactionExec.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/TransactionMulti.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/TransactionUnwatch.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/TransactionWatch.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ZSetAdd.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ZSetCardinality.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ZSetCount.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ZSetIncrementBy.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ZSetIntersectionStore.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ZSetLexCount.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ZSetRange.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ZSetRangeByLex.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ZSetRangeByScore.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ZSetRank.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ZSetRemove.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ZSetRemoveRangeByLex.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ZSetRemoveRangeByRank.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ZSetRemoveRangeByScore.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ZSetReverseRange.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ZSetReverseRangeByLex.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ZSetReverseRangeByScore.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ZSetReverseRank.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ZSetScan.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ZSetScore.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Command/ZSetUnionStore.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/CommunicationException.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Configuration/ClusterOption.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Configuration/ConnectionFactoryOption.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Configuration/ExceptionsOption.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Configuration/OptionInterface.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Configuration/Options.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Configuration/OptionsInterface.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Configuration/PrefixOption.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Configuration/ProfileOption.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Configuration/ReplicationOption.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Connection/AbstractConnection.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Connection/Aggregate/ClusterInterface.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Connection/Aggregate/MasterSlaveReplication.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Connection/Aggregate/PredisCluster.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Connection/Aggregate/RedisCluster.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Connection/Aggregate/ReplicationInterface.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Connection/Aggregate/SentinelReplication.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Connection/AggregateConnectionInterface.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Connection/CompositeConnectionInterface.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Connection/CompositeStreamConnection.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Connection/ConnectionException.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Connection/ConnectionInterface.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Connection/Factory.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Connection/FactoryInterface.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Connection/NodeConnectionInterface.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Connection/Parameters.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Connection/ParametersInterface.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Connection/PhpiredisSocketConnection.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Connection/PhpiredisStreamConnection.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Connection/StreamConnection.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Connection/WebdisConnection.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Monitor/Consumer.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/NotSupportedException.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Pipeline/Atomic.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Pipeline/ConnectionErrorProof.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Pipeline/FireAndForget.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Pipeline/Pipeline.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/PredisException.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Profile/Factory.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Profile/ProfileInterface.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Profile/RedisProfile.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Profile/RedisUnstable.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Profile/RedisVersion200.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Profile/RedisVersion220.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Profile/RedisVersion240.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Profile/RedisVersion260.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Profile/RedisVersion280.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Profile/RedisVersion300.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Profile/RedisVersion320.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Protocol/ProtocolException.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Protocol/ProtocolProcessorInterface.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Protocol/RequestSerializerInterface.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Protocol/ResponseReaderInterface.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Protocol/Text/CompositeProtocolProcessor.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Protocol/Text/Handler/BulkResponse.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Protocol/Text/Handler/ErrorResponse.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Protocol/Text/Handler/IntegerResponse.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Protocol/Text/Handler/MultiBulkResponse.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Protocol/Text/Handler/ResponseHandlerInterface.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Protocol/Text/Handler/StatusResponse.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Protocol/Text/Handler/StreamableMultiBulkResponse.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Protocol/Text/ProtocolProcessor.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Protocol/Text/RequestSerializer.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Protocol/Text/ResponseReader.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/PubSub/AbstractConsumer.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/PubSub/Consumer.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/PubSub/DispatcherLoop.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Replication/MissingMasterException.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Replication/ReplicationStrategy.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Replication/RoleException.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Response/Error.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Response/ErrorInterface.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Response/Iterator/MultiBulk.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Response/Iterator/MultiBulkIterator.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Response/Iterator/MultiBulkTuple.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Response/ResponseInterface.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Response/ServerException.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Response/Status.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Session/Handler.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Transaction/AbortedMultiExecException.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Transaction/MultiExec.php +0 -0
- {includes → dependencies/vendor/predis}/predis/src/Transaction/MultiExecState.php +0 -0
- includes/admin-page.css +0 -32
- includes/admin-page.js +0 -16
- includes/admin-page.php +0 -174
- includes/class-autoloader.php +145 -0
- includes/class-plugin.php +721 -0
- includes/class-qm-collector.php +93 -0
- includes/class-qm-output.php +88 -0
- includes/class-ui.php +54 -0
- includes/{wp-cli-commands.php → cli/class-commands.php} +23 -17
- includes/diagnostics.php +0 -110
- includes/object-cache.php +932 -620
assets/css/admin.css
ADDED
@@ -0,0 +1,160 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#rediscache .content-column {
|
2 |
+
margin-bottom: 3em;
|
3 |
+
}
|
4 |
+
|
5 |
+
#rediscache .sidebar-column h6 {
|
6 |
+
margin: 5px 0;
|
7 |
+
padding: 10px 0;
|
8 |
+
font-size: 13px;
|
9 |
+
line-height: 19px;
|
10 |
+
border-bottom: 1px solid #ccc;
|
11 |
+
}
|
12 |
+
|
13 |
+
#rediscache .section {
|
14 |
+
display: none;
|
15 |
+
}
|
16 |
+
|
17 |
+
#rediscache .section.active {
|
18 |
+
display: block;
|
19 |
+
}
|
20 |
+
|
21 |
+
#rediscache .form-table th,
|
22 |
+
#rediscache .form-table td {
|
23 |
+
padding-top: 7px;
|
24 |
+
padding-bottom: 7px;
|
25 |
+
}
|
26 |
+
|
27 |
+
#rediscache .form-table td ul {
|
28 |
+
margin: 0;
|
29 |
+
}
|
30 |
+
|
31 |
+
#rediscache .form-table .description.is-notice {
|
32 |
+
max-width: 25rem;
|
33 |
+
color: #d54e21;
|
34 |
+
}
|
35 |
+
|
36 |
+
#rediscache .card ul {
|
37 |
+
list-style: circle;
|
38 |
+
padding-left: 25px;
|
39 |
+
}
|
40 |
+
|
41 |
+
#rediscache .section-metrics .card {
|
42 |
+
min-width: auto;
|
43 |
+
max-width: 100%;
|
44 |
+
padding: 0;
|
45 |
+
}
|
46 |
+
|
47 |
+
#rediscache #redis-stats-chart {
|
48 |
+
height: 300px;
|
49 |
+
}
|
50 |
+
|
51 |
+
#rediscache .section-diagnostics p {
|
52 |
+
margin-top: 20px;
|
53 |
+
}
|
54 |
+
|
55 |
+
#rediscache .section-diagnostics textarea {
|
56 |
+
width: 100%;
|
57 |
+
}
|
58 |
+
|
59 |
+
#rediscache .compatiblity {
|
60 |
+
display: flex;
|
61 |
+
max-width: 520px;
|
62 |
+
margin-bottom: 5px;
|
63 |
+
color: #666;
|
64 |
+
}
|
65 |
+
|
66 |
+
#rediscache .compatiblity + ul {
|
67 |
+
margin-top: 0;
|
68 |
+
padding-left: 50px;
|
69 |
+
list-style: circle;
|
70 |
+
max-width: 470px;
|
71 |
+
color: #666;
|
72 |
+
}
|
73 |
+
|
74 |
+
#rediscache .compatiblity .dashicons {
|
75 |
+
margin-right: 5px;
|
76 |
+
}
|
77 |
+
|
78 |
+
@media screen and (min-width: 1200px) {
|
79 |
+
#rediscache .columns {
|
80 |
+
display: flex;
|
81 |
+
}
|
82 |
+
|
83 |
+
#rediscache .content-column {
|
84 |
+
flex-grow: 1;
|
85 |
+
}
|
86 |
+
|
87 |
+
#rediscache .sidebar-column {
|
88 |
+
max-width: 400px;
|
89 |
+
margin-left: 20px;
|
90 |
+
}
|
91 |
+
}
|
92 |
+
|
93 |
+
#dashboard_rediscache .inside {
|
94 |
+
margin: 0;
|
95 |
+
padding: 0;
|
96 |
+
}
|
97 |
+
|
98 |
+
#widget-redis-stats ul {
|
99 |
+
margin: 11px 11px 0;
|
100 |
+
text-align: center;
|
101 |
+
}
|
102 |
+
|
103 |
+
#widget-redis-stats ul li {
|
104 |
+
display: inline;
|
105 |
+
}
|
106 |
+
|
107 |
+
#widget-redis-stats ul a {
|
108 |
+
padding: 0 5px;
|
109 |
+
color: #72777c;
|
110 |
+
font-weight: bold;
|
111 |
+
text-decoration: none;
|
112 |
+
}
|
113 |
+
|
114 |
+
#widget-redis-stats ul a:hover,
|
115 |
+
#widget-redis-stats ul a.active {
|
116 |
+
color: #444;
|
117 |
+
}
|
118 |
+
|
119 |
+
#redis-stats-chart .apexcharts-annotations {
|
120 |
+
display: none;
|
121 |
+
opacity: 0.75;
|
122 |
+
}
|
123 |
+
|
124 |
+
#redis-stats-chart .apexcharts-tooltip,
|
125 |
+
#redis-stats-chart .apexcharts-tooltip-title {
|
126 |
+
font-size: 13px;
|
127 |
+
}
|
128 |
+
|
129 |
+
#redis-stats-chart .apexcharts-tooltip {
|
130 |
+
display: flex;
|
131 |
+
width: 100%;
|
132 |
+
justify-content: center;
|
133 |
+
border: none;
|
134 |
+
box-shadow: none;
|
135 |
+
background: #fff;
|
136 |
+
}
|
137 |
+
|
138 |
+
#redis-stats-chart .apexcharts-tooltip-title {
|
139 |
+
margin-bottom: 0;
|
140 |
+
padding: 0 15px;
|
141 |
+
border-bottom: none;
|
142 |
+
background-color: transparent;
|
143 |
+
font-weight: bold;
|
144 |
+
font-variant-numeric: tabular-nums;
|
145 |
+
}
|
146 |
+
|
147 |
+
#redis-stats-chart .apexcharts-tooltip-series-group {
|
148 |
+
display: flex;
|
149 |
+
padding-bottom: 0;
|
150 |
+
padding: 0 15px;
|
151 |
+
}
|
152 |
+
|
153 |
+
#redis-stats-chart .apexcharts-tooltip-y-group {
|
154 |
+
padding: 0;
|
155 |
+
}
|
156 |
+
|
157 |
+
#redis-stats-chart .apexcharts-tooltip-text-value {
|
158 |
+
margin-left: 0;
|
159 |
+
font-variant-numeric: tabular-nums;
|
160 |
+
}
|
assets/js/admin.js
ADDED
@@ -0,0 +1,461 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
( function ( $, root, undefined ) {
|
2 |
+
root.rediscache = root.rediscache || {};
|
3 |
+
var rediscache = root.rediscache;
|
4 |
+
|
5 |
+
$.extend( rediscache, {
|
6 |
+
metrics: {
|
7 |
+
computed: null,
|
8 |
+
names: {
|
9 |
+
h: 'hits',
|
10 |
+
m: 'misses',
|
11 |
+
r: 'ratio',
|
12 |
+
b: 'bytes',
|
13 |
+
t: 'time',
|
14 |
+
c: 'calls',
|
15 |
+
},
|
16 |
+
},
|
17 |
+
chart: null,
|
18 |
+
chart_defaults: {
|
19 |
+
noData: {
|
20 |
+
text: root.rediscache_metrics
|
21 |
+
? rediscache.l10n.no_data
|
22 |
+
: rediscache.l10n.no_cache,
|
23 |
+
align: 'center',
|
24 |
+
verticalAlign: 'middle',
|
25 |
+
offsetY: -25,
|
26 |
+
style: {
|
27 |
+
color: '#72777c',
|
28 |
+
fontSize: '14px',
|
29 |
+
fontFamily: 'inherit',
|
30 |
+
}
|
31 |
+
},
|
32 |
+
stroke: {
|
33 |
+
width: [2, 2],
|
34 |
+
curve: 'smooth',
|
35 |
+
dashArray: [0, 8],
|
36 |
+
},
|
37 |
+
colors: [
|
38 |
+
'#0096dd',
|
39 |
+
'#72777c',
|
40 |
+
],
|
41 |
+
annotations: {
|
42 |
+
texts: [{ x: '15%', y: '30%', fontSize: '20px', fontWeight: 600, fontFamily: 'inherit', foreColor: '#72777c' }],
|
43 |
+
},
|
44 |
+
chart: {
|
45 |
+
type: 'line',
|
46 |
+
height: '100%',
|
47 |
+
toolbar: { show: false },
|
48 |
+
zoom: { enabled: false },
|
49 |
+
animations: { enabled: false }
|
50 |
+
},
|
51 |
+
dataLabels: {
|
52 |
+
enabled: false,
|
53 |
+
},
|
54 |
+
legend: {
|
55 |
+
show: false,
|
56 |
+
},
|
57 |
+
fill: {
|
58 |
+
opacity: [0.25, 1],
|
59 |
+
},
|
60 |
+
xaxis: {
|
61 |
+
type: 'datetime',
|
62 |
+
labels: {
|
63 |
+
format: 'HH:mm',
|
64 |
+
datetimeUTC: false,
|
65 |
+
style: { colors: '#72777c', fontSize: '13px', fontFamily: 'inherit' },
|
66 |
+
},
|
67 |
+
tooltip: { enabled: false },
|
68 |
+
},
|
69 |
+
yaxis: {
|
70 |
+
type: 'numeric',
|
71 |
+
tickAmount: 4,
|
72 |
+
min: 0,
|
73 |
+
labels: {
|
74 |
+
style: { colors: '#72777c', fontSize: '13px', fontFamily: 'inherit' },
|
75 |
+
formatter: function (value) {
|
76 |
+
return Math.round(value);
|
77 |
+
},
|
78 |
+
},
|
79 |
+
},
|
80 |
+
tooltip: {
|
81 |
+
fixed: {
|
82 |
+
enabled: true,
|
83 |
+
position: 'bottomLeft',
|
84 |
+
offsetY: 15,
|
85 |
+
offsetX: 0,
|
86 |
+
},
|
87 |
+
}
|
88 |
+
},
|
89 |
+
templates: {
|
90 |
+
tooltip_title: _.template(
|
91 |
+
'<div class="apexcharts-tooltip-title"><%- title %></div>'
|
92 |
+
),
|
93 |
+
series_group: _.template(
|
94 |
+
'<div class="apexcharts-tooltip-series-group">' +
|
95 |
+
' <span class="apexcharts-tooltip-marker" style="background-color: <%- color %>;"></span>' +
|
96 |
+
' <div class="apexcharts-tooltip-text">' +
|
97 |
+
' <div class="apexcharts-tooltip-y-group">' +
|
98 |
+
' <span class="apexcharts-tooltip-text-label"><%- name %>:</span>' +
|
99 |
+
' <span class="apexcharts-tooltip-text-value"><%- value %></span>' +
|
100 |
+
' </div>' +
|
101 |
+
' </div>' +
|
102 |
+
'</div>'
|
103 |
+
),
|
104 |
+
series_pro: _.template(
|
105 |
+
'<div class="apexcharts-tooltip-series-group">' +
|
106 |
+
' <span class="apexcharts-tooltip-marker" style="background-color: <%- color %>;"></span>' +
|
107 |
+
' <div class="apexcharts-tooltip-text">' +
|
108 |
+
' <div class="apexcharts-tooltip-y-group">' +
|
109 |
+
' <span class="apexcharts-tooltip-text-label"><%- name %></span>' +
|
110 |
+
' </div>' +
|
111 |
+
' </div>' +
|
112 |
+
'</div>'
|
113 |
+
),
|
114 |
+
}
|
115 |
+
} );
|
116 |
+
|
117 |
+
// Build the charts by deep extending the chart defaults
|
118 |
+
$.extend( rediscache, {
|
119 |
+
charts: {
|
120 |
+
time: $.extend( true, {}, rediscache.chart_defaults, {
|
121 |
+
yaxis: {
|
122 |
+
labels: {
|
123 |
+
formatter: function ( value ) {
|
124 |
+
return Math.round(value) + ' ms';
|
125 |
+
},
|
126 |
+
},
|
127 |
+
},
|
128 |
+
tooltip: {
|
129 |
+
custom: function ({ series, seriesIndex, dataPointIndex, w }) {
|
130 |
+
return [
|
131 |
+
rediscache.templates.tooltip_title({
|
132 |
+
title: new Date( w.globals.seriesX[seriesIndex][dataPointIndex] ).toTimeString().slice( 0, 5 ),
|
133 |
+
}),
|
134 |
+
rediscache.templates.series_group({
|
135 |
+
color: rediscache.chart_defaults.colors[0],
|
136 |
+
name: w.globals.seriesNames[0],
|
137 |
+
value: series[0][dataPointIndex].toFixed(2) + ' ms',
|
138 |
+
}),
|
139 |
+
rediscache.templates.series_pro({
|
140 |
+
color: rediscache.chart_defaults.colors[1],
|
141 |
+
name: rediscache.l10n.pro,
|
142 |
+
}),
|
143 |
+
].join('');
|
144 |
+
},
|
145 |
+
},
|
146 |
+
} ),
|
147 |
+
bytes: $.extend( true, {}, rediscache.chart_defaults, {
|
148 |
+
yaxis: {
|
149 |
+
labels: {
|
150 |
+
formatter: function ( value ) {
|
151 |
+
return Math.round( value / 1024 ) + ' KB';
|
152 |
+
},
|
153 |
+
},
|
154 |
+
},
|
155 |
+
tooltip: {
|
156 |
+
custom: function ({ series, seriesIndex, dataPointIndex, w }) {
|
157 |
+
return [
|
158 |
+
rediscache.templates.tooltip_title({
|
159 |
+
title: new Date( w.globals.seriesX[seriesIndex][dataPointIndex] ).toTimeString().slice( 0, 5 ),
|
160 |
+
}),
|
161 |
+
rediscache.templates.series_group({
|
162 |
+
color: rediscache.chart_defaults.colors[0],
|
163 |
+
name: w.globals.seriesNames[0],
|
164 |
+
value: Math.round( series[0][dataPointIndex] / 1024 ) + ' kb',
|
165 |
+
}),
|
166 |
+
rediscache.templates.series_pro({
|
167 |
+
color: rediscache.chart_defaults.colors[1],
|
168 |
+
name: rediscache.l10n.pro,
|
169 |
+
}),
|
170 |
+
].join('');
|
171 |
+
},
|
172 |
+
},
|
173 |
+
} ),
|
174 |
+
ratio: $.extend( true, {}, rediscache.chart_defaults, {
|
175 |
+
yaxis: {
|
176 |
+
max: 100,
|
177 |
+
labels: {
|
178 |
+
formatter: function ( value ) {
|
179 |
+
return Math.round( value ) + '%';
|
180 |
+
},
|
181 |
+
},
|
182 |
+
},
|
183 |
+
tooltip: {
|
184 |
+
custom: function ({ series, seriesIndex, dataPointIndex, w }) {
|
185 |
+
return [
|
186 |
+
rediscache.templates.tooltip_title({
|
187 |
+
title: new Date( w.globals.seriesX[seriesIndex][dataPointIndex] ).toTimeString().slice( 0, 5 ),
|
188 |
+
}),
|
189 |
+
rediscache.templates.series_group({
|
190 |
+
color: rediscache.chart_defaults.colors[0],
|
191 |
+
name: w.globals.seriesNames[0],
|
192 |
+
value: Math.round( series[0][dataPointIndex] * 100 ) / 100 + '%',
|
193 |
+
}),
|
194 |
+
].join('');
|
195 |
+
},
|
196 |
+
},
|
197 |
+
} ),
|
198 |
+
calls: $.extend( true, {}, rediscache.chart_defaults, {
|
199 |
+
tooltip: {
|
200 |
+
custom: function ({ series, seriesIndex, dataPointIndex, w }) {
|
201 |
+
return [
|
202 |
+
rediscache.templates.tooltip_title({
|
203 |
+
title: new Date( w.globals.seriesX[seriesIndex][dataPointIndex] ).toTimeString().slice( 0, 5 ),
|
204 |
+
}),
|
205 |
+
rediscache.templates.series_group({
|
206 |
+
color: rediscache.chart_defaults.colors[0],
|
207 |
+
name: w.globals.seriesNames[0],
|
208 |
+
value: Math.round( series[0][dataPointIndex] ),
|
209 |
+
}),
|
210 |
+
rediscache.templates.series_pro({
|
211 |
+
color: rediscache.chart_defaults.colors[1],
|
212 |
+
name: rediscache.l10n.pro,
|
213 |
+
}),
|
214 |
+
].join('');
|
215 |
+
},
|
216 |
+
},
|
217 |
+
} ),
|
218 |
+
},
|
219 |
+
} );
|
220 |
+
|
221 |
+
var compute_metrics = function ( raw_metrics, metric_names ) {
|
222 |
+
var metrics = {};
|
223 |
+
|
224 |
+
// parse raw metrics in blocks of minutes
|
225 |
+
for ( var entry in raw_metrics ) {
|
226 |
+
var values = {};
|
227 |
+
var timestamp = raw_metrics[ entry ];
|
228 |
+
var minute = ( timestamp - timestamp % 60 ) * 1000;
|
229 |
+
|
230 |
+
entry.split( ';' ).forEach(
|
231 |
+
function ( value ) {
|
232 |
+
var metric = value.split( '=' );
|
233 |
+
|
234 |
+
if ( metric_names[ metric[0] ] ) {
|
235 |
+
values[ metric_names[ metric[0] ] ] = Number( metric[1] );
|
236 |
+
}
|
237 |
+
}
|
238 |
+
);
|
239 |
+
|
240 |
+
if ( ! metrics[ minute ] ) {
|
241 |
+
metrics[ minute ] = [];
|
242 |
+
}
|
243 |
+
|
244 |
+
metrics[ minute ].push( values );
|
245 |
+
}
|
246 |
+
|
247 |
+
// calculate median value for each block
|
248 |
+
for ( var entry in metrics ) {
|
249 |
+
if ( metrics[ entry ].length === 1 ) {
|
250 |
+
metrics[ entry ] = metrics[ entry ].shift();
|
251 |
+
continue;
|
252 |
+
}
|
253 |
+
|
254 |
+
var medians = {};
|
255 |
+
|
256 |
+
for ( var key in metric_names ) {
|
257 |
+
var name = metric_names[ key ];
|
258 |
+
|
259 |
+
medians[ name ] = compute_median(
|
260 |
+
metrics[ entry ].map(
|
261 |
+
function ( metric ) {
|
262 |
+
return metric[ name ];
|
263 |
+
}
|
264 |
+
)
|
265 |
+
)
|
266 |
+
}
|
267 |
+
|
268 |
+
metrics[ entry ] = medians;
|
269 |
+
}
|
270 |
+
|
271 |
+
var computed = [];
|
272 |
+
|
273 |
+
for ( var timestamp in metrics ) {
|
274 |
+
var entry = metrics[ timestamp ];
|
275 |
+
|
276 |
+
entry.date = Number( timestamp );
|
277 |
+
entry.time = entry.time * 1000;
|
278 |
+
|
279 |
+
computed.push( entry );
|
280 |
+
}
|
281 |
+
|
282 |
+
computed.sort(
|
283 |
+
function( a, b ) {
|
284 |
+
return a.date - b.date;
|
285 |
+
}
|
286 |
+
);
|
287 |
+
|
288 |
+
return computed.length < 2 ? [] : computed;
|
289 |
+
};
|
290 |
+
|
291 |
+
var compute_median = function ( numbers ) {
|
292 |
+
var median = 0;
|
293 |
+
var numsLen = numbers.length;
|
294 |
+
|
295 |
+
numbers.sort();
|
296 |
+
|
297 |
+
if ( numsLen % 2 === 0 ) {
|
298 |
+
median = ( numbers[ numsLen / 2 - 1 ] + numbers[ numsLen / 2 ] ) / 2;
|
299 |
+
} else {
|
300 |
+
median = numbers[ ( numsLen - 1 ) / 2 ];
|
301 |
+
}
|
302 |
+
|
303 |
+
return median;
|
304 |
+
};
|
305 |
+
|
306 |
+
var render_chart = function ( id ) {
|
307 |
+
if ( rediscache.chart ) {
|
308 |
+
rediscache.chart.updateOptions( rediscache.charts[id] );
|
309 |
+
return;
|
310 |
+
}
|
311 |
+
|
312 |
+
var chart = new ApexCharts(
|
313 |
+
document.querySelector( '#redis-stats-chart' ),
|
314 |
+
rediscache.charts[id]
|
315 |
+
);
|
316 |
+
|
317 |
+
chart.render();
|
318 |
+
root.rediscache.chart = chart;
|
319 |
+
};
|
320 |
+
|
321 |
+
var setup_charts = function () {
|
322 |
+
var time = rediscache.metrics.computed.map(
|
323 |
+
function ( entry ) {
|
324 |
+
return [entry.date, entry.time];
|
325 |
+
}
|
326 |
+
);
|
327 |
+
|
328 |
+
var bytes = rediscache.metrics.computed.map(
|
329 |
+
function ( entry ) {
|
330 |
+
return [entry.date, entry.bytes];
|
331 |
+
}
|
332 |
+
)
|
333 |
+
|
334 |
+
var ratio = rediscache.metrics.computed.map(
|
335 |
+
function ( entry ) {
|
336 |
+
return [entry.date, entry.ratio];
|
337 |
+
}
|
338 |
+
);
|
339 |
+
|
340 |
+
var calls = rediscache.metrics.computed.map(
|
341 |
+
function ( entry ) {
|
342 |
+
return [entry.date, entry.calls];
|
343 |
+
}
|
344 |
+
);
|
345 |
+
|
346 |
+
rediscache.charts.time.series = [{
|
347 |
+
name: 'Time',
|
348 |
+
type: 'area',
|
349 |
+
data: time,
|
350 |
+
}, {
|
351 |
+
name: 'Pro',
|
352 |
+
type: 'line',
|
353 |
+
data: time.map(
|
354 |
+
function ( entry ) {
|
355 |
+
return [entry[0], entry[1] * 0.5];
|
356 |
+
}
|
357 |
+
),
|
358 |
+
} ];
|
359 |
+
|
360 |
+
rediscache.charts.bytes.series = [{
|
361 |
+
name: rediscache.l10n.bytes,
|
362 |
+
type: 'area',
|
363 |
+
data: bytes,
|
364 |
+
}, {
|
365 |
+
name: 'Pro',
|
366 |
+
type: 'line',
|
367 |
+
data: bytes.map(
|
368 |
+
function ( entry ) {
|
369 |
+
return [entry[0], entry[1] * 0.3];
|
370 |
+
}
|
371 |
+
),
|
372 |
+
} ];
|
373 |
+
|
374 |
+
rediscache.charts.ratio.series = [{
|
375 |
+
name: rediscache.l10n.ratio,
|
376 |
+
type: 'area',
|
377 |
+
data: ratio,
|
378 |
+
}];
|
379 |
+
|
380 |
+
rediscache.charts.calls.series = [{
|
381 |
+
name: rediscache.l10n.calls,
|
382 |
+
type: 'area',
|
383 |
+
data: calls,
|
384 |
+
}, {
|
385 |
+
name: 'Pro',
|
386 |
+
type: 'line',
|
387 |
+
data: calls.map(
|
388 |
+
function ( entry ) {
|
389 |
+
return [entry[0], Math.round( entry[1] / 50 ) + 5];
|
390 |
+
}
|
391 |
+
),
|
392 |
+
} ];
|
393 |
+
};
|
394 |
+
|
395 |
+
// executed on page load
|
396 |
+
$(function () {
|
397 |
+
|
398 |
+
var $tabs = $('#redis-tabs');
|
399 |
+
$tabs.find('a').on(
|
400 |
+
'click.redis',
|
401 |
+
function () {
|
402 |
+
var $this = $(this),
|
403 |
+
$target = $($this.data('target'));
|
404 |
+
$tabs.find('a').removeClass('nav-tab-active');
|
405 |
+
$('.section').removeClass('active');
|
406 |
+
$target.addClass('active');
|
407 |
+
$this.addClass('nav-tab-active');
|
408 |
+
$(window).trigger('redis-tab-change', $target);
|
409 |
+
}
|
410 |
+
);
|
411 |
+
|
412 |
+
var tabHash = window.location.hash.replace('#top#', '');
|
413 |
+
if ( -1 !== tabHash.search('#top') ) {
|
414 |
+
tabHash = window.location.hash.replace('#top%23', '');
|
415 |
+
}
|
416 |
+
if ( '' !== tabHash && '#' !== tabHash.charAt(0) ) {
|
417 |
+
$tabs.find('a').removeClass('nav-tab-active');
|
418 |
+
$('.section').removeClass('active');
|
419 |
+
$('#' + tabHash).addClass('active');
|
420 |
+
$('#' + tabHash + '-tab').addClass('nav-tab-active').trigger('click.redis');
|
421 |
+
}
|
422 |
+
|
423 |
+
if ($('#widget-redis-stats').length) {
|
424 |
+
rediscache.metrics.computed = compute_metrics(
|
425 |
+
root.rediscache_metrics,
|
426 |
+
rediscache.metrics.names
|
427 |
+
);
|
428 |
+
|
429 |
+
setup_charts();
|
430 |
+
render_chart('time');
|
431 |
+
}
|
432 |
+
|
433 |
+
$('#widget-redis-stats ul a').on(
|
434 |
+
'click.redis',
|
435 |
+
function (event) {
|
436 |
+
event.preventDefault();
|
437 |
+
|
438 |
+
$('#widget-redis-stats .active').removeClass('active');
|
439 |
+
$(this).blur().addClass('active');
|
440 |
+
|
441 |
+
render_chart(
|
442 |
+
$(event.target).data('chart')
|
443 |
+
);
|
444 |
+
}
|
445 |
+
);
|
446 |
+
|
447 |
+
$('.notice.is-dismissible[data-dismissible]').on(
|
448 |
+
'click.roc-dismiss-notice',
|
449 |
+
'.notice-dismiss',
|
450 |
+
function (event) {
|
451 |
+
event.preventDefault();
|
452 |
+
|
453 |
+
$.post(ajaxurl, {
|
454 |
+
notice: $(this).parent().attr('data-dismissible'),
|
455 |
+
action: 'roc_dismiss_notice',
|
456 |
+
});
|
457 |
+
}
|
458 |
+
);
|
459 |
+
});
|
460 |
+
|
461 |
+
} ( window[rediscache.jQuery], window ) );
|
assets/js/apexcharts.min.js
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*!
|
2 |
+
* ApexCharts v3.19.3
|
3 |
+
* (c) 2018-2020 Juned Chhipa
|
4 |
+
* Released under the MIT License.
|
5 |
+
*/
|
6 |
+
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t=t||self).ApexCharts=e()}(this,(function(){"use strict";function t(e){return(t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(e)}function e(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function i(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,"value"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}function a(t,e,a){return e&&i(t.prototype,e),a&&i(t,a),t}function s(t,e,i){return e in t?Object.defineProperty(t,e,{value:i,enumerable:!0,configurable:!0,writable:!0}):t[e]=i,t}function r(t,e){var i=Object.keys(t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(t);e&&(a=a.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),i.push.apply(i,a)}return i}function n(t){for(var e=1;e<arguments.length;e++){var i=null!=arguments[e]?arguments[e]:{};e%2?r(Object(i),!0).forEach((function(e){s(t,e,i[e])})):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(i)):r(Object(i)).forEach((function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(i,e))}))}return t}function o(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function");t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,writable:!0,configurable:!0}}),e&&h(t,e)}function l(t){return(l=Object.setPrototypeOf?Object.getPrototypeOf:function(t){return t.__proto__||Object.getPrototypeOf(t)})(t)}function h(t,e){return(h=Object.setPrototypeOf||function(t,e){return t.__proto__=e,t})(t,e)}function c(t,e){return!e||"object"!=typeof e&&"function"!=typeof e?function(t){if(void 0===t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return t}(t):e}function d(t){var e=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Date.prototype.toString.call(Reflect.construct(Date,[],(function(){}))),!0}catch(t){return!1}}();return function(){var i,a=l(t);if(e){var s=l(this).constructor;i=Reflect.construct(a,arguments,s)}else i=a.apply(this,arguments);return c(this,i)}}function g(t){return function(t){if(Array.isArray(t))return u(t)}(t)||function(t){if("undefined"!=typeof Symbol&&Symbol.iterator in Object(t))return Array.from(t)}(t)||function(t,e){if(!t)return;if("string"==typeof t)return u(t,e);var i=Object.prototype.toString.call(t).slice(8,-1);"Object"===i&&t.constructor&&(i=t.constructor.name);if("Map"===i||"Set"===i)return Array.from(t);if("Arguments"===i||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(i))return u(t,e)}(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function u(t,e){(null==e||e>t.length)&&(e=t.length);for(var i=0,a=new Array(e);i<e;i++)a[i]=t[i];return a}var f=function(){function i(){e(this,i)}return a(i,[{key:"shadeRGBColor",value:function(t,e){var i=e.split(","),a=t<0?0:255,s=t<0?-1*t:t,r=parseInt(i[0].slice(4),10),n=parseInt(i[1],10),o=parseInt(i[2],10);return"rgb("+(Math.round((a-r)*s)+r)+","+(Math.round((a-n)*s)+n)+","+(Math.round((a-o)*s)+o)+")"}},{key:"shadeHexColor",value:function(t,e){var i=parseInt(e.slice(1),16),a=t<0?0:255,s=t<0?-1*t:t,r=i>>16,n=i>>8&255,o=255&i;return"#"+(16777216+65536*(Math.round((a-r)*s)+r)+256*(Math.round((a-n)*s)+n)+(Math.round((a-o)*s)+o)).toString(16).slice(1)}},{key:"shadeColor",value:function(t,e){return i.isColorHex(e)?this.shadeHexColor(t,e):this.shadeRGBColor(t,e)}}],[{key:"bind",value:function(t,e){return function(){return t.apply(e,arguments)}}},{key:"isObject",value:function(e){return e&&"object"===t(e)&&!Array.isArray(e)&&null!=e}},{key:"listToArray",value:function(t){var e,i=[];for(e=0;e<t.length;e++)i[e]=t[e];return i}},{key:"extend",value:function(t,e){var i=this;"function"!=typeof Object.assign&&(Object.assign=function(t){if(null==t)throw new TypeError("Cannot convert undefined or null to object");for(var e=Object(t),i=1;i<arguments.length;i++){var a=arguments[i];if(null!=a)for(var s in a)a.hasOwnProperty(s)&&(e[s]=a[s])}return e});var a=Object.assign({},t);return this.isObject(t)&&this.isObject(e)&&Object.keys(e).forEach((function(r){i.isObject(e[r])&&r in t?a[r]=i.extend(t[r],e[r]):Object.assign(a,s({},r,e[r]))})),a}},{key:"extendArray",value:function(t,e){var a=[];return t.map((function(t){a.push(i.extend(e,t))})),t=a}},{key:"monthMod",value:function(t){return t%12}},{key:"clone",value:function(e){if("[object Array]"===Object.prototype.toString.call(e)){for(var i=[],a=0;a<e.length;a++)i[a]=this.clone(e[a]);return i}if("[object Null]"===Object.prototype.toString.call(e))return null;if("[object Date]"===Object.prototype.toString.call(e))return e;if("object"===t(e)){var s={};for(var r in e)e.hasOwnProperty(r)&&(s[r]=this.clone(e[r]));return s}return e}},{key:"log10",value:function(t){return Math.log(t)/Math.LN10}},{key:"roundToBase10",value:function(t){return Math.pow(10,Math.floor(Math.log10(t)))}},{key:"roundToBase",value:function(t,e){return Math.pow(e,Math.floor(Math.log(t)/Math.log(e)))}},{key:"parseNumber",value:function(t){return null===t?t:parseFloat(t)}},{key:"randomId",value:function(){return(Math.random()+1).toString(36).substring(4)}},{key:"noExponents",value:function(t){var e=String(t).split(/[eE]/);if(1===e.length)return e[0];var i="",a=t<0?"-":"",s=e[0].replace(".",""),r=Number(e[1])+1;if(r<0){for(i=a+"0.";r++;)i+="0";return i+s.replace(/^-/,"")}for(r-=s.length;r--;)i+="0";return s+i}},{key:"getDimensions",value:function(t){var e=getComputedStyle(t),i=[],a=t.clientHeight,s=t.clientWidth;return a-=parseFloat(e.paddingTop)+parseFloat(e.paddingBottom),s-=parseFloat(e.paddingLeft)+parseFloat(e.paddingRight),i.push(s),i.push(a),i}},{key:"getBoundingClientRect",value:function(t){var e=t.getBoundingClientRect();return{top:e.top,right:e.right,bottom:e.bottom,left:e.left,width:t.clientWidth,height:t.clientHeight,x:e.left,y:e.top}}},{key:"getLargestStringFromArr",value:function(t){return t.reduce((function(t,e){return Array.isArray(e)&&(e=e.reduce((function(t,e){return t.length>e.length?t:e}))),t.length>e.length?t:e}),0)}},{key:"hexToRgba",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"#999999",e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:.6;"#"!==t.substring(0,1)&&(t="#999999");var i=t.replace("#","");i=i.match(new RegExp("(.{"+i.length/3+"})","g"));for(var a=0;a<i.length;a++)i[a]=parseInt(1===i[a].length?i[a]+i[a]:i[a],16);return void 0!==e&&i.push(e),"rgba("+i.join(",")+")"}},{key:"getOpacityFromRGBA",value:function(t){return parseFloat(t.replace(/^.*,(.+)\)/,"$1"))}},{key:"rgb2hex",value:function(t){return(t=t.match(/^rgba?[\s+]?\([\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?/i))&&4===t.length?"#"+("0"+parseInt(t[1],10).toString(16)).slice(-2)+("0"+parseInt(t[2],10).toString(16)).slice(-2)+("0"+parseInt(t[3],10).toString(16)).slice(-2):""}},{key:"isColorHex",value:function(t){return/(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)|(^#[0-9A-F]{8}$)/i.test(t)}},{key:"polarToCartesian",value:function(t,e,i,a){var s=(a-90)*Math.PI/180;return{x:t+i*Math.cos(s),y:e+i*Math.sin(s)}}},{key:"escapeString",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"x",i=t.toString().slice();return i=i.replace(/[` ~!@#$%^&*()_|+\-=?;:'",.<>{}[\]\\/]/gi,e)}},{key:"negToZero",value:function(t){return t<0?0:t}},{key:"moveIndexInArray",value:function(t,e,i){if(i>=t.length)for(var a=i-t.length+1;a--;)t.push(void 0);return t.splice(i,0,t.splice(e,1)[0]),t}},{key:"extractNumber",value:function(t){return parseFloat(t.replace(/[^\d.]*/g,""))}},{key:"findAncestor",value:function(t,e){for(;(t=t.parentElement)&&!t.classList.contains(e););return t}},{key:"setELstyles",value:function(t,e){for(var i in e)e.hasOwnProperty(i)&&(t.style.key=e[i])}},{key:"isNumber",value:function(t){return!isNaN(t)&&parseFloat(Number(t))===t&&!isNaN(parseInt(t,10))}},{key:"isFloat",value:function(t){return Number(t)===t&&t%1!=0}},{key:"isSafari",value:function(){return/^((?!chrome|android).)*safari/i.test(navigator.userAgent)}},{key:"isFirefox",value:function(){return navigator.userAgent.toLowerCase().indexOf("firefox")>-1}},{key:"isIE11",value:function(){if(-1!==window.navigator.userAgent.indexOf("MSIE")||window.navigator.appVersion.indexOf("Trident/")>-1)return!0}},{key:"isIE",value:function(){var t=window.navigator.userAgent,e=t.indexOf("MSIE ");if(e>0)return parseInt(t.substring(e+5,t.indexOf(".",e)),10);if(t.indexOf("Trident/")>0){var i=t.indexOf("rv:");return parseInt(t.substring(i+3,t.indexOf(".",i)),10)}var a=t.indexOf("Edge/");return a>0&&parseInt(t.substring(a+5,t.indexOf(".",a)),10)}}]),i}(),p=function(){function t(i){e(this,t),this.ctx=i,this.w=i.w}return a(t,[{key:"getDefaultFilter",value:function(t,e){var i=this.w;t.unfilter(!0),(new window.SVG.Filter).size("120%","180%","-5%","-40%"),"none"!==i.config.states.normal.filter?this.applyFilter(t,e,i.config.states.normal.filter.type,i.config.states.normal.filter.value):i.config.chart.dropShadow.enabled&&this.dropShadow(t,i.config.chart.dropShadow,e)}},{key:"addNormalFilter",value:function(t,e){var i=this.w;i.config.chart.dropShadow.enabled&&!t.node.classList.contains("apexcharts-marker")&&this.dropShadow(t,i.config.chart.dropShadow,e)}},{key:"addLightenFilter",value:function(t,e,i){var a=this,s=this.w,r=i.intensity;if(!f.isFirefox()){t.unfilter(!0);new window.SVG.Filter;t.filter((function(t){var i=s.config.chart.dropShadow;(i.enabled?a.addShadow(t,e,i):t).componentTransfer({rgb:{type:"linear",slope:1.5,intercept:r}})})),t.filterer.node.setAttribute("filterUnits","userSpaceOnUse"),this._scaleFilterSize(t.filterer.node)}}},{key:"addDarkenFilter",value:function(t,e,i){var a=this,s=this.w,r=i.intensity;if(!f.isFirefox()){t.unfilter(!0);new window.SVG.Filter;t.filter((function(t){var i=s.config.chart.dropShadow;(i.enabled?a.addShadow(t,e,i):t).componentTransfer({rgb:{type:"linear",slope:r}})})),t.filterer.node.setAttribute("filterUnits","userSpaceOnUse"),this._scaleFilterSize(t.filterer.node)}}},{key:"applyFilter",value:function(t,e,i){var a=arguments.length>3&&void 0!==arguments[3]?arguments[3]:.5;switch(i){case"none":this.addNormalFilter(t,e);break;case"lighten":this.addLightenFilter(t,e,{intensity:a});break;case"darken":this.addDarkenFilter(t,e,{intensity:a})}}},{key:"addShadow",value:function(t,e,i){var a=i.blur,s=i.top,r=i.left,n=i.color,o=i.opacity,l=t.flood(Array.isArray(n)?n[e]:n,o).composite(t.sourceAlpha,"in").offset(r,s).gaussianBlur(a).merge(t.source);return t.blend(t.source,l)}},{key:"dropShadow",value:function(t,e){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0,a=e.top,s=e.left,r=e.blur,n=e.color,o=e.opacity,l=e.noUserSpaceOnUse,h=this.w;return t.unfilter(!0),f.isIE()&&"radialBar"===h.config.chart.type||(n=Array.isArray(n)?n[i]:n,t.filter((function(t){var e=null;e=f.isSafari()||f.isFirefox()||f.isIE()?t.flood(n,o).composite(t.sourceAlpha,"in").offset(s,a).gaussianBlur(r):t.flood(n,o).composite(t.sourceAlpha,"in").offset(s,a).gaussianBlur(r).merge(t.source),t.blend(t.source,e)})),l||t.filterer.node.setAttribute("filterUnits","userSpaceOnUse"),this._scaleFilterSize(t.filterer.node)),t}},{key:"setSelectionFilter",value:function(t,e,i){var a=this.w;if(void 0!==a.globals.selectedDataPoints[e]&&a.globals.selectedDataPoints[e].indexOf(i)>-1){t.node.setAttribute("selected",!0);var s=a.config.states.active.filter;"none"!==s&&this.applyFilter(t,e,s.type,s.value)}}},{key:"_scaleFilterSize",value:function(t){!function(e){for(var i in e)e.hasOwnProperty(i)&&t.setAttribute(i,e[i])}({width:"200%",height:"200%",x:"-50%",y:"-50%"})}}]),t}(),x=function(){function t(i){e(this,t),this.ctx=i,this.w=i.w,this.setEasingFunctions()}return a(t,[{key:"setEasingFunctions",value:function(){var t;if(!this.w.globals.easing){switch(this.w.config.chart.animations.easing){case"linear":t="-";break;case"easein":t="<";break;case"easeout":t=">";break;case"easeinout":t="<>";break;case"swing":t=function(t){var e=1.70158;return(t-=1)*t*((e+1)*t+e)+1};break;case"bounce":t=function(t){return t<1/2.75?7.5625*t*t:t<2/2.75?7.5625*(t-=1.5/2.75)*t+.75:t<2.5/2.75?7.5625*(t-=2.25/2.75)*t+.9375:7.5625*(t-=2.625/2.75)*t+.984375};break;case"elastic":t=function(t){return t===!!t?t:Math.pow(2,-10*t)*Math.sin((t-.075)*(2*Math.PI)/.3)+1};break;default:t="<>"}this.w.globals.easing=t}}},{key:"animateLine",value:function(t,e,i,a){t.attr(e).animate(a).attr(i)}},{key:"animateCircleRadius",value:function(t,e,i,a,s,r){e||(e=0),t.attr({r:e}).animate(a,s).attr({r:i}).afterAll((function(){r()}))}},{key:"animateCircle",value:function(t,e,i,a,s){t.attr({r:e.r,cx:e.cx,cy:e.cy}).animate(a,s).attr({r:i.r,cx:i.cx,cy:i.cy})}},{key:"animateRect",value:function(t,e,i,a,s){t.attr(e).animate(a).attr(i).afterAll((function(){return s()}))}},{key:"animatePathsGradually",value:function(t){var e=t.el,i=t.realIndex,a=t.j,s=t.fill,r=t.pathFrom,n=t.pathTo,o=t.speed,l=t.delay,h=this.w,c=0;h.config.chart.animations.animateGradually.enabled&&(c=h.config.chart.animations.animateGradually.delay),h.config.chart.animations.dynamicAnimation.enabled&&h.globals.dataChanged&&"bar"!==h.config.chart.type&&(c=0),this.morphSVG(e,i,a,"line"!==h.config.chart.type||h.globals.comboCharts?s:"stroke",r,n,o,l*c)}},{key:"showDelayedElements",value:function(){this.w.globals.delayedElements.forEach((function(t){t.el.classList.remove("apexcharts-element-hidden")}))}},{key:"animationCompleted",value:function(t){var e=this.w;e.globals.animationEnded||(e.globals.animationEnded=!0,this.showDelayedElements(),"function"==typeof e.config.chart.events.animationEnd&&e.config.chart.events.animationEnd(this.ctx,{el:t,w:e}))}},{key:"morphSVG",value:function(t,e,i,a,s,r,n,o){var l=this,h=this.w;s||(s=t.attr("pathFrom")),r||(r=t.attr("pathTo"));var c=function(t){return"radar"===h.config.chart.type&&(n=1),"M 0 ".concat(h.globals.gridHeight)};(!s||s.indexOf("undefined")>-1||s.indexOf("NaN")>-1)&&(s=c()),(r.indexOf("undefined")>-1||r.indexOf("NaN")>-1)&&(r=c()),h.globals.shouldAnimate||(n=1),t.plot(s).animate(1,h.globals.easing,o).plot(s).animate(n,h.globals.easing,o).plot(r).afterAll((function(){f.isNumber(i)?i===h.globals.series[h.globals.maxValsInArrayIndex].length-2&&h.globals.shouldAnimate&&l.animationCompleted(t):"none"!==a&&h.globals.shouldAnimate&&(!h.globals.comboCharts&&e===h.globals.series.length-1||h.globals.comboCharts)&&l.animationCompleted(t),l.showDelayedElements()}))}}]),t}(),b=function(){function t(i){e(this,t),this.ctx=i,this.w=i.w}return a(t,[{key:"drawLine",value:function(t,e,i,a){var s=arguments.length>4&&void 0!==arguments[4]?arguments[4]:"#a8a8a8",r=arguments.length>5&&void 0!==arguments[5]?arguments[5]:0,n=arguments.length>6&&void 0!==arguments[6]?arguments[6]:null,o=this.w,l=o.globals.dom.Paper.line().attr({x1:t,y1:e,x2:i,y2:a,stroke:s,"stroke-dasharray":r,"stroke-width":n});return l}},{key:"drawRect",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0,e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0,a=arguments.length>3&&void 0!==arguments[3]?arguments[3]:0,s=arguments.length>4&&void 0!==arguments[4]?arguments[4]:0,r=arguments.length>5&&void 0!==arguments[5]?arguments[5]:"#fefefe",n=arguments.length>6&&void 0!==arguments[6]?arguments[6]:1,o=arguments.length>7&&void 0!==arguments[7]?arguments[7]:null,l=arguments.length>8&&void 0!==arguments[8]?arguments[8]:null,h=arguments.length>9&&void 0!==arguments[9]?arguments[9]:0,c=this.w,d=c.globals.dom.Paper.rect();return d.attr({x:t,y:e,width:i>0?i:0,height:a>0?a:0,rx:s,ry:s,opacity:n,"stroke-width":null!==o?o:0,stroke:null!==l?l:"none","stroke-dasharray":h}),d.node.setAttribute("fill",r),d}},{key:"drawPolygon",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"#e1e1e1",i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:1,a=arguments.length>3&&void 0!==arguments[3]?arguments[3]:"none",s=this.w,r=s.globals.dom.Paper.polygon(t).attr({fill:a,stroke:e,"stroke-width":i});return r}},{key:"drawCircle",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,i=this.w,a=i.globals.dom.Paper.circle(2*t);return null!==e&&a.attr(e),a}},{key:"drawPath",value:function(t){var e=t.d,i=void 0===e?"":e,a=t.stroke,s=void 0===a?"#a8a8a8":a,r=t.strokeWidth,n=void 0===r?1:r,o=t.fill,l=t.fillOpacity,h=void 0===l?1:l,c=t.strokeOpacity,d=void 0===c?1:c,g=t.classes,u=t.strokeLinecap,f=void 0===u?null:u,p=t.strokeDashArray,x=void 0===p?0:p,b=this.w;return null===f&&(f=b.config.stroke.lineCap),(i.indexOf("undefined")>-1||i.indexOf("NaN")>-1)&&(i="M 0 ".concat(b.globals.gridHeight)),b.globals.dom.Paper.path(i).attr({fill:o,"fill-opacity":h,stroke:s,"stroke-opacity":d,"stroke-linecap":f,"stroke-width":n,"stroke-dasharray":x,class:g})}},{key:"group",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null,e=this.w,i=e.globals.dom.Paper.group();return null!==t&&i.attr(t),i}},{key:"move",value:function(t,e){var i=["M",t,e].join(" ");return i}},{key:"line",value:function(t,e){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null,a=null;return null===i?a=["L",t,e].join(" "):"H"===i?a=["H",t].join(" "):"V"===i&&(a=["V",e].join(" ")),a}},{key:"curve",value:function(t,e,i,a,s,r){var n=["C",t,e,i,a,s,r].join(" ");return n}},{key:"quadraticCurve",value:function(t,e,i,a){return["Q",t,e,i,a].join(" ")}},{key:"arc",value:function(t,e,i,a,s,r,n){var o=arguments.length>7&&void 0!==arguments[7]&&arguments[7],l="A";o&&(l="a");var h=[l,t,e,i,a,s,r,n].join(" ");return h}},{key:"renderPaths",value:function(t){var e,i=t.j,a=t.realIndex,s=t.pathFrom,r=t.pathTo,o=t.stroke,l=t.strokeWidth,h=t.strokeLinecap,c=t.fill,d=t.animationDelay,g=t.initialSpeed,u=t.dataChangeSpeed,f=t.className,b=t.shouldClipToGrid,m=void 0===b||b,v=t.bindEventsOnPaths,y=void 0===v||v,w=t.drawShadow,k=void 0===w||w,A=this.w,S=new p(this.ctx),C=new x(this.ctx),L=this.w.config.chart.animations.enabled,P=L&&this.w.config.chart.animations.dynamicAnimation.enabled,T=!!(L&&!A.globals.resized||P&&A.globals.dataChanged&&A.globals.shouldAnimate);T?e=s:(e=r,A.globals.animationEnded=!0);var z=A.config.stroke.dashArray,I=0;I=Array.isArray(z)?z[a]:A.config.stroke.dashArray;var M=this.drawPath({d:e,stroke:o,strokeWidth:l,fill:c,fillOpacity:1,classes:f,strokeLinecap:h,strokeDashArray:I});if(M.attr("index",a),m&&M.attr({"clip-path":"url(#gridRectMask".concat(A.globals.cuid,")")}),"none"!==A.config.states.normal.filter.type)S.getDefaultFilter(M,a);else if(A.config.chart.dropShadow.enabled&&k&&(!A.config.chart.dropShadow.enabledOnSeries||A.config.chart.dropShadow.enabledOnSeries&&-1!==A.config.chart.dropShadow.enabledOnSeries.indexOf(a))){var E=A.config.chart.dropShadow;S.dropShadow(M,E,a)}y&&(M.node.addEventListener("mouseenter",this.pathMouseEnter.bind(this,M)),M.node.addEventListener("mouseleave",this.pathMouseLeave.bind(this,M)),M.node.addEventListener("mousedown",this.pathMouseDown.bind(this,M))),M.attr({pathTo:r,pathFrom:s});var X={el:M,j:i,realIndex:a,pathFrom:s,pathTo:r,fill:c,strokeWidth:l,delay:d};return!L||A.globals.resized||A.globals.dataChanged?!A.globals.resized&&A.globals.dataChanged||C.showDelayedElements():C.animatePathsGradually(n(n({},X),{},{speed:g})),A.globals.dataChanged&&P&&T&&C.animatePathsGradually(n(n({},X),{},{speed:u})),M}},{key:"drawPattern",value:function(t,e,i){var a=arguments.length>3&&void 0!==arguments[3]?arguments[3]:"#a8a8a8",s=arguments.length>4&&void 0!==arguments[4]?arguments[4]:0,r=this.w,n=r.globals.dom.Paper.pattern(e,i,(function(r){"horizontalLines"===t?r.line(0,0,i,0).stroke({color:a,width:s+1}):"verticalLines"===t?r.line(0,0,0,e).stroke({color:a,width:s+1}):"slantedLines"===t?r.line(0,0,e,i).stroke({color:a,width:s}):"squares"===t?r.rect(e,i).fill("none").stroke({color:a,width:s}):"circles"===t&&r.circle(e).fill("none").stroke({color:a,width:s})}));return n}},{key:"drawGradient",value:function(t,e,i,a,s){var r,n=arguments.length>5&&void 0!==arguments[5]?arguments[5]:null,o=arguments.length>6&&void 0!==arguments[6]?arguments[6]:null,l=arguments.length>7&&void 0!==arguments[7]?arguments[7]:null,h=arguments.length>8&&void 0!==arguments[8]?arguments[8]:0,c=this.w;e.length<9&&0===e.indexOf("#")&&(e=f.hexToRgba(e,a)),i.length<9&&0===i.indexOf("#")&&(i=f.hexToRgba(i,s));var d=0,g=1,u=1,p=null;null!==o&&(d=void 0!==o[0]?o[0]/100:0,g=void 0!==o[1]?o[1]/100:1,u=void 0!==o[2]?o[2]/100:1,p=void 0!==o[3]?o[3]/100:null);var x=!("donut"!==c.config.chart.type&&"pie"!==c.config.chart.type&&"polarArea"!==c.config.chart.type&&"bubble"!==c.config.chart.type);if(r=null===l||0===l.length?c.globals.dom.Paper.gradient(x?"radial":"linear",(function(t){t.at(d,e,a),t.at(g,i,s),t.at(u,i,s),null!==p&&t.at(p,e,a)})):c.globals.dom.Paper.gradient(x?"radial":"linear",(function(t){(Array.isArray(l[h])?l[h]:l).forEach((function(e){t.at(e.offset/100,e.color,e.opacity)}))})),x){var b=c.globals.gridWidth/2,m=c.globals.gridHeight/2;"bubble"!==c.config.chart.type?r.attr({gradientUnits:"userSpaceOnUse",cx:b,cy:m,r:n}):r.attr({cx:.5,cy:.5,r:.8,fx:.2,fy:.2})}else"vertical"===t?r.from(0,0).to(0,1):"diagonal"===t?r.from(0,0).to(1,1):"horizontal"===t?r.from(0,1).to(1,1):"diagonal2"===t&&r.from(1,0).to(0,1);return r}},{key:"drawText",value:function(t){var e,i=t.x,a=t.y,s=t.text,r=t.textAnchor,n=t.fontSize,o=t.fontFamily,l=t.fontWeight,h=t.foreColor,c=t.opacity,d=t.cssClass,g=void 0===d?"":d,u=t.isPlainText,f=void 0===u||u,p=this.w;return void 0===s&&(s=""),r||(r="start"),h&&h.length||(h=p.config.chart.foreColor),o=o||p.config.chart.fontFamily,l=l||"regular",(e=Array.isArray(s)?p.globals.dom.Paper.text((function(t){for(var e=0;e<s.length;e++)0===e?t.tspan(s[e]):t.tspan(s[e]).newLine()})):f?p.globals.dom.Paper.plain(s):p.globals.dom.Paper.text((function(t){return t.tspan(s)}))).attr({x:i,y:a,"text-anchor":r,"dominant-baseline":"auto","font-size":n,"font-family":o,"font-weight":l,fill:h,class:"apexcharts-text "+g}),e.node.style.fontFamily=o,e.node.style.opacity=c,e}},{key:"drawMarker",value:function(t,e,i){t=t||0;var a=i.pSize||0,s=null;if("square"===i.shape){var r=void 0===i.pRadius?a/2:i.pRadius;null!==e&&a||(a=0,r=0);var n=1.2*a+r,o=this.drawRect(n,n,n,n,r);o.attr({x:t-n/2,y:e-n/2,cx:t,cy:e,class:i.class?i.class:"",fill:i.pointFillColor,"fill-opacity":i.pointFillOpacity?i.pointFillOpacity:1,stroke:i.pointStrokeColor,"stroke-width":i.pWidth?i.pWidth:0,"stroke-opacity":i.pointStrokeOpacity?i.pointStrokeOpacity:1}),s=o}else"circle"!==i.shape&&i.shape||(f.isNumber(e)||(a=0,e=0),s=this.drawCircle(a,{cx:t,cy:e,class:i.class?i.class:"",stroke:i.pointStrokeColor,fill:i.pointFillColor,"fill-opacity":i.pointFillOpacity?i.pointFillOpacity:1,"stroke-width":i.pWidth?i.pWidth:0,"stroke-opacity":i.pointStrokeOpacity?i.pointStrokeOpacity:1}));return s}},{key:"pathMouseEnter",value:function(t,e){var i=this.w,a=new p(this.ctx),s=parseInt(t.node.getAttribute("index"),10),r=parseInt(t.node.getAttribute("j"),10);if("function"==typeof i.config.chart.events.dataPointMouseEnter&&i.config.chart.events.dataPointMouseEnter(e,this.ctx,{seriesIndex:s,dataPointIndex:r,w:i}),this.ctx.events.fireEvent("dataPointMouseEnter",[e,this.ctx,{seriesIndex:s,dataPointIndex:r,w:i}]),("none"===i.config.states.active.filter.type||"true"!==t.node.getAttribute("selected"))&&"none"!==i.config.states.hover.filter.type&&"none"!==i.config.states.active.filter.type&&!i.globals.isTouchDevice){var n=i.config.states.hover.filter;a.applyFilter(t,s,n.type,n.value)}}},{key:"pathMouseLeave",value:function(t,e){var i=this.w,a=new p(this.ctx),s=parseInt(t.node.getAttribute("index"),10),r=parseInt(t.node.getAttribute("j"),10);"function"==typeof i.config.chart.events.dataPointMouseLeave&&i.config.chart.events.dataPointMouseLeave(e,this.ctx,{seriesIndex:s,dataPointIndex:r,w:i}),this.ctx.events.fireEvent("dataPointMouseLeave",[e,this.ctx,{seriesIndex:s,dataPointIndex:r,w:i}]),"none"!==i.config.states.active.filter.type&&"true"===t.node.getAttribute("selected")||"none"!==i.config.states.hover.filter.type&&a.getDefaultFilter(t,s)}},{key:"pathMouseDown",value:function(t,e){var i=this.w,a=new p(this.ctx),s=parseInt(t.node.getAttribute("index"),10),r=parseInt(t.node.getAttribute("j"),10),n="false";if("true"===t.node.getAttribute("selected")){if(t.node.setAttribute("selected","false"),i.globals.selectedDataPoints[s].indexOf(r)>-1){var o=i.globals.selectedDataPoints[s].indexOf(r);i.globals.selectedDataPoints[s].splice(o,1)}}else{if(!i.config.states.active.allowMultipleDataPointsSelection&&i.globals.selectedDataPoints.length>0){i.globals.selectedDataPoints=[];var l=i.globals.dom.Paper.select(".apexcharts-series path").members,h=i.globals.dom.Paper.select(".apexcharts-series circle, .apexcharts-series rect").members,c=function(t){Array.prototype.forEach.call(t,(function(t){t.node.setAttribute("selected","false"),a.getDefaultFilter(t,s)}))};c(l),c(h)}t.node.setAttribute("selected","true"),n="true",void 0===i.globals.selectedDataPoints[s]&&(i.globals.selectedDataPoints[s]=[]),i.globals.selectedDataPoints[s].push(r)}if("true"===n){var d=i.config.states.active.filter;"none"!==d&&a.applyFilter(t,s,d.type,d.value)}else"none"!==i.config.states.active.filter.type&&a.getDefaultFilter(t,s);"function"==typeof i.config.chart.events.dataPointSelection&&i.config.chart.events.dataPointSelection(e,this.ctx,{selectedDataPoints:i.globals.selectedDataPoints,seriesIndex:s,dataPointIndex:r,w:i}),e&&this.ctx.events.fireEvent("dataPointSelection",[e,this.ctx,{selectedDataPoints:i.globals.selectedDataPoints,seriesIndex:s,dataPointIndex:r,w:i}])}},{key:"rotateAroundCenter",value:function(t){var e=t.getBBox();return{x:e.x+e.width/2,y:e.y+e.height/2}}},{key:"getTextRects",value:function(t,e,i,a){var s=!(arguments.length>4&&void 0!==arguments[4])||arguments[4],r=this.w,n=this.drawText({x:-200,y:-200,text:t,textAnchor:"start",fontSize:e,fontFamily:i,foreColor:"#fff",opacity:0});a&&n.attr("transform",a),r.globals.dom.Paper.add(n);var o=n.bbox();return s||(o=n.node.getBoundingClientRect()),n.remove(),{width:o.width,height:o.height}}},{key:"placeTextWithEllipsis",value:function(t,e,i){if("function"==typeof t.getComputedTextLength&&(t.textContent=e,e.length>0&&t.getComputedTextLength()>=i/.8)){for(var a=e.length-3;a>0;a-=3)if(t.getSubStringLength(0,a)<=i/.8)return void(t.textContent=e.substring(0,a)+"...");t.textContent="."}}}],[{key:"setAttrs",value:function(t,e){for(var i in e)e.hasOwnProperty(i)&&t.setAttribute(i,e[i])}}]),t}(),m=function(){function t(i){e(this,t),this.w=i.w,this.annoCtx=i}return a(t,[{key:"setOrientations",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,i=this.w;if("vertical"===t.label.orientation){var a=null!==e?e:0,s=i.globals.dom.baseEl.querySelector(".apexcharts-xaxis-annotations .apexcharts-xaxis-annotation-label[rel='".concat(a,"']"));if(null!==s){var r=s.getBoundingClientRect();s.setAttribute("x",parseFloat(s.getAttribute("x"))-r.height+4),"top"===t.label.position?s.setAttribute("y",parseFloat(s.getAttribute("y"))+r.width):s.setAttribute("y",parseFloat(s.getAttribute("y"))-r.width);var n=this.annoCtx.graphics.rotateAroundCenter(s),o=n.x,l=n.y;s.setAttribute("transform","rotate(-90 ".concat(o," ").concat(l,")"))}}}},{key:"addBackgroundToAnno",value:function(t,e){var i=this.w;if(!e.label.text||e.label.text&&!e.label.text.trim())return null;var a=i.globals.dom.baseEl.querySelector(".apexcharts-grid").getBoundingClientRect(),s=t.getBoundingClientRect(),r=e.label.style.padding.left,n=e.label.style.padding.right,o=e.label.style.padding.top,l=e.label.style.padding.bottom;"vertical"===e.label.orientation&&(o=e.label.style.padding.left,l=e.label.style.padding.right,r=e.label.style.padding.top,n=e.label.style.padding.bottom);var h=s.left-a.left-r,c=s.top-a.top-o,d=this.annoCtx.graphics.drawRect(h-i.globals.barPadForNumericAxis,c,s.width+r+n,s.height+o+l,e.label.borderRadius,e.label.style.background,1,e.label.borderWidth,e.label.borderColor,0);return e.id&&d.node.classList.add(e.id),d}},{key:"annotationsBackground",value:function(){var t=this,e=this.w,i=function(i,a,s){var r=e.globals.dom.baseEl.querySelector(".apexcharts-".concat(s,"-annotations .apexcharts-").concat(s,"-annotation-label[rel='").concat(a,"']"));if(r){var n=r.parentNode,o=t.addBackgroundToAnno(r,i);o&&n.insertBefore(o.node,r)}};e.config.annotations.xaxis.map((function(t,e){i(t,e,"xaxis")})),e.config.annotations.yaxis.map((function(t,e){i(t,e,"yaxis")})),e.config.annotations.points.map((function(t,e){i(t,e,"point")}))}},{key:"makeAnnotationDraggable",value:function(t,e,i){var a=this.w.config.annotations[e][i];t.draggable().on("dragend",(function(t){var e=t.target.getAttribute("x"),i=t.target.getAttribute("y"),s=t.target.getAttribute("cx"),r=t.target.getAttribute("cy");a.x=e,a.y=i,s&&r&&(a.x=s,a.y=r)})),t.node.addEventListener("mousedown",(function(e){e.stopPropagation(),t.selectize({pointSize:8,rotationPoint:!1,pointType:"rect"}),t.resize().on("resizedone",(function(t){var e=t.target.getAttribute("width"),i=t.target.getAttribute("height"),s=t.target.getAttribute("r");a.width=e,a.height=i,s&&(a.radius=s)}))}))}},{key:"getStringX",value:function(t){var e=this.w,i=t;e.config.xaxis.convertedCatToNumeric&&e.globals.categoryLabels.length&&(t=e.globals.categoryLabels.indexOf(t)+1);var a=e.globals.labels.indexOf(t),s=e.globals.dom.baseEl.querySelector(".apexcharts-xaxis-texts-g text:nth-child("+(a+1)+")");return s&&(i=parseFloat(s.getAttribute("x"))),i}}]),t}(),v=function(){function t(i){e(this,t),this.w=i.w,this.annoCtx=i,this.invertAxis=this.annoCtx.invertAxis}return a(t,[{key:"addXaxisAnnotation",value:function(t,e,i){var a=this.w,s=this.invertAxis?a.globals.minY:a.globals.minX,r=this.invertAxis?a.globals.maxY:a.globals.maxX,n=this.invertAxis?a.globals.yRange[0]:a.globals.xRange,o=(t.x-s)/(n/a.globals.gridWidth);this.annoCtx.inversedReversedAxis&&(o=(r-t.x)/(n/a.globals.gridWidth));var l=t.label.text;"category"!==a.config.xaxis.type&&!a.config.xaxis.convertedCatToNumeric||this.invertAxis||a.globals.dataFormatXNumeric||(o=this.annoCtx.helpers.getStringX(t.x));var h=t.strokeDashArray;if(f.isNumber(o)){if(null===t.x2||void 0===t.x2){var c=this.annoCtx.graphics.drawLine(o+t.offsetX,0+t.offsetY,o+t.offsetX,a.globals.gridHeight+t.offsetY,t.borderColor,h,t.borderWidth);e.appendChild(c.node),t.id&&c.node.classList.add(t.id)}else{var d=(t.x2-s)/(n/a.globals.gridWidth);if(this.annoCtx.inversedReversedAxis&&(d=(r-t.x2)/(n/a.globals.gridWidth)),"category"!==a.config.xaxis.type&&!a.config.xaxis.convertedCatToNumeric||this.invertAxis||a.globals.dataFormatXNumeric||(d=this.annoCtx.helpers.getStringX(t.x2)),d<o){var g=o;o=d,d=g}var u=this.annoCtx.graphics.drawRect(o+t.offsetX,0+t.offsetY,d-o,a.globals.gridHeight+t.offsetY,0,t.fillColor,t.opacity,1,t.borderColor,h);u.node.classList.add("apexcharts-annotation-rect"),u.attr("clip-path","url(#gridRectMask".concat(a.globals.cuid,")")),e.appendChild(u.node),t.id&&u.node.classList.add(t.id)}var p="top"===t.label.position?4:a.globals.gridHeight,x=this.annoCtx.graphics.getTextRects(l,parseFloat(t.label.style.fontSize)),b=this.annoCtx.graphics.drawText({x:o+t.label.offsetX,y:p+t.label.offsetY-("vertical"===t.label.orientation?"top"===t.label.position?x.width/2-12:-x.width/2:0),text:l,textAnchor:t.label.textAnchor,fontSize:t.label.style.fontSize,fontFamily:t.label.style.fontFamily,fontWeight:t.label.style.fontWeight,foreColor:t.label.style.color,cssClass:"apexcharts-xaxis-annotation-label ".concat(t.label.style.cssClass," ").concat(t.id?t.id:"")});b.attr({rel:i}),e.appendChild(b.node),this.annoCtx.helpers.setOrientations(t,i)}}},{key:"drawXAxisAnnotations",value:function(){var t=this,e=this.w,i=this.annoCtx.graphics.group({class:"apexcharts-xaxis-annotations"});return e.config.annotations.xaxis.map((function(e,a){t.addXaxisAnnotation(e,i.node,a)})),i}}]),t}(),y=function(){function t(i){e(this,t),this.ctx=i,this.w=i.w}return a(t,[{key:"getStackedSeriesTotals",value:function(){var t=this.w,e=[];if(0===t.globals.series.length)return e;for(var i=0;i<t.globals.series[t.globals.maxValsInArrayIndex].length;i++){for(var a=0,s=0;s<t.globals.series.length;s++)void 0!==t.globals.series[s][i]&&(a+=t.globals.series[s][i]);e.push(a)}return t.globals.stackedSeriesTotals=e,e}},{key:"getSeriesTotalByIndex",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null;return null===t?this.w.config.series.reduce((function(t,e){return t+e}),0):this.w.globals.series[t].reduce((function(t,e){return t+e}),0)}},{key:"isSeriesNull",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null;return 0===(null===t?this.w.config.series.filter((function(t){return null!==t})):this.w.config.series[t].data.filter((function(t){return null!==t}))).length}},{key:"seriesHaveSameValues",value:function(t){return this.w.globals.series[t].every((function(t,e,i){return t===i[0]}))}},{key:"getCategoryLabels",value:function(t){var e=this.w,i=t.slice();return e.config.xaxis.convertedCatToNumeric&&(i=t.map((function(t){return e.config.xaxis.labels.formatter(t-e.globals.minX+1)}))),i}},{key:"getLargestSeries",value:function(){var t=this.w;t.globals.maxValsInArrayIndex=t.globals.series.map((function(t){return t.length})).indexOf(Math.max.apply(Math,t.globals.series.map((function(t){return t.length}))))}},{key:"getLargestMarkerSize",value:function(){var t=this.w,e=0;return t.globals.markers.size.forEach((function(t){e=Math.max(e,t)})),t.globals.markers.largestSize=e,e}},{key:"getSeriesTotals",value:function(){var t=this.w;t.globals.seriesTotals=t.globals.series.map((function(t,e){var i=0;if(Array.isArray(t))for(var a=0;a<t.length;a++)i+=t[a];else i+=t;return i}))}},{key:"getSeriesTotalsXRange",value:function(t,e){var i=this.w;return i.globals.series.map((function(a,s){for(var r=0,n=0;n<a.length;n++)i.globals.seriesX[s][n]>t&&i.globals.seriesX[s][n]<e&&(r+=a[n]);return r}))}},{key:"getPercentSeries",value:function(){var t=this.w;t.globals.seriesPercent=t.globals.series.map((function(e,i){var a=[];if(Array.isArray(e))for(var s=0;s<e.length;s++){var r=t.globals.stackedSeriesTotals[s],n=0;r&&(n=100*e[s]/r),a.push(n)}else{var o=100*e/t.globals.seriesTotals.reduce((function(t,e){return t+e}),0);a.push(o)}return a}))}},{key:"getCalculatedRatios",value:function(){var t,e,i,a,s=this.w.globals,r=[],n=0,o=[],l=.1,h=0;if(s.yRange=[],s.isMultipleYAxis)for(var c=0;c<s.minYArr.length;c++)s.yRange.push(Math.abs(s.minYArr[c]-s.maxYArr[c])),o.push(0);else s.yRange.push(Math.abs(s.minY-s.maxY));s.xRange=Math.abs(s.maxX-s.minX),s.zRange=Math.abs(s.maxZ-s.minZ);for(var d=0;d<s.yRange.length;d++)r.push(s.yRange[d]/s.gridHeight);if(e=s.xRange/s.gridWidth,i=Math.abs(s.initialMaxX-s.initialMinX)/s.gridWidth,t=s.yRange/s.gridWidth,a=s.xRange/s.gridHeight,(n=s.zRange/s.gridHeight*16)||(n=1),s.minY!==Number.MIN_VALUE&&0!==Math.abs(s.minY)&&(s.hasNegs=!0),s.isMultipleYAxis){o=[];for(var g=0;g<r.length;g++)o.push(-s.minYArr[g]/r[g])}else o.push(-s.minY/r[0]),s.minY!==Number.MIN_VALUE&&0!==Math.abs(s.minY)&&(l=-s.minY/t,h=s.minX/e);return{yRatio:r,invertedYRatio:t,zRatio:n,xRatio:e,initialXRatio:i,invertedXRatio:a,baseLineInvertedY:l,baseLineY:o,baseLineX:h}}},{key:"getLogSeries",value:function(t){var e=this,i=this.w;return i.globals.seriesLog=t.map((function(t,a){return i.config.yaxis[a]&&i.config.yaxis[a].logarithmic?t.map((function(t){return null===t?null:e.getLogVal(t,a)})):t})),i.globals.invalidLogScale?t:i.globals.seriesLog}},{key:"getLogVal",value:function(t,e){var i=this.w;return(Math.log(t)-Math.log(i.globals.minYArr[e]))/(Math.log(i.globals.maxYArr[e])-Math.log(i.globals.minYArr[e]))}},{key:"getLogYRatios",value:function(t){var e=this,i=this.w,a=this.w.globals;return a.yLogRatio=t.slice(),a.logYRange=a.yRange.map((function(t,s){if(i.config.yaxis[s]&&e.w.config.yaxis[s].logarithmic){var r,n=-Number.MAX_VALUE,o=Number.MIN_VALUE;return a.seriesLog.forEach((function(t,e){t.forEach((function(t){i.config.yaxis[e]&&i.config.yaxis[e].logarithmic&&(n=Math.max(t,n),o=Math.min(t,o))}))})),r=Math.pow(a.yRange[s],Math.abs(o-n)/a.yRange[s]),a.yLogRatio[s]=r/a.gridHeight,r}})),a.invalidLogScale?t.slice():a.yLogRatio}}],[{key:"checkComboSeries",value:function(t){var e=!1,i=0;return t.length&&void 0!==t[0].type&&(e=!0,t.forEach((function(t){"bar"!==t.type&&"column"!==t.type&&"candlestick"!==t.type||i++}))),{comboBarCount:i,comboCharts:e}}},{key:"extendArrayProps",value:function(t,e,i){return e.yaxis&&(e=t.extendYAxis(e,i)),e.annotations&&(e.annotations.yaxis&&(e=t.extendYAxisAnnotations(e)),e.annotations.xaxis&&(e=t.extendXAxisAnnotations(e)),e.annotations.points&&(e=t.extendPointAnnotations(e))),e}}]),t}(),w=function(){function t(i){e(this,t),this.w=i.w,this.annoCtx=i}return a(t,[{key:"addYaxisAnnotation",value:function(t,e,i){var a,s=this.w,r=t.strokeDashArray,n=this._getY1Y2("y1",t),o=t.label.text;if(null===t.y2||void 0===t.y2){var l=this.annoCtx.graphics.drawLine(0+t.offsetX,n+t.offsetY,s.globals.gridWidth+t.offsetX,n+t.offsetY,t.borderColor,r,t.borderWidth);e.appendChild(l.node),t.id&&l.node.classList.add(t.id)}else{if((a=this._getY1Y2("y2",t))>n){var h=n;n=a,a=h}var c=this.annoCtx.graphics.drawRect(0+t.offsetX,a+t.offsetY,s.globals.gridWidth+t.offsetX,n-a,0,t.fillColor,t.opacity,1,t.borderColor,r);c.node.classList.add("apexcharts-annotation-rect"),c.attr("clip-path","url(#gridRectMask".concat(s.globals.cuid,")")),e.appendChild(c.node),t.id&&c.node.classList.add(t.id)}var d="right"===t.label.position?s.globals.gridWidth:0,g=this.annoCtx.graphics.drawText({x:d+t.label.offsetX,y:(a||n)+t.label.offsetY-3,text:o,textAnchor:t.label.textAnchor,fontSize:t.label.style.fontSize,fontFamily:t.label.style.fontFamily,fontWeight:t.label.style.fontWeight,foreColor:t.label.style.color,cssClass:"apexcharts-yaxis-annotation-label ".concat(t.label.style.cssClass," ").concat(t.id?t.id:"")});g.attr({rel:i}),e.appendChild(g.node)}},{key:"_getY1Y2",value:function(t,e){var i,a="y1"===t?e.y:e.y2,s=this.w;if(this.annoCtx.invertAxis){var r=s.globals.labels.indexOf(a);s.config.xaxis.convertedCatToNumeric&&(r=s.globals.categoryLabels.indexOf(a));var n=s.globals.dom.baseEl.querySelector(".apexcharts-yaxis-texts-g text:nth-child("+(r+1)+")");n&&(i=parseFloat(n.getAttribute("y")))}else{var o;if(s.config.yaxis[e.yAxisIndex].logarithmic)o=(a=new y(this.annoCtx.ctx).getLogVal(a,e.yAxisIndex))/s.globals.yLogRatio[e.yAxisIndex];else o=(a-s.globals.minYArr[e.yAxisIndex])/(s.globals.yRange[e.yAxisIndex]/s.globals.gridHeight);i=s.globals.gridHeight-o,s.config.yaxis[e.yAxisIndex]&&s.config.yaxis[e.yAxisIndex].reversed&&(i=o)}return i}},{key:"drawYAxisAnnotations",value:function(){var t=this,e=this.w,i=this.annoCtx.graphics.group({class:"apexcharts-yaxis-annotations"});return e.config.annotations.yaxis.map((function(e,a){t.addYaxisAnnotation(e,i.node,a)})),i}}]),t}(),k=function(){function t(i){e(this,t),this.w=i.w,this.annoCtx=i}return a(t,[{key:"addPointAnnotation",value:function(t,e,i){var a=this.w,s=0,r=0,n=0;this.annoCtx.invertAxis&&console.warn("Point annotation is not supported in horizontal bar charts.");var o,l=parseFloat(t.y);if("string"==typeof t.x){var h=a.globals.labels.indexOf(t.x);a.config.xaxis.convertedCatToNumeric&&(h=a.globals.categoryLabels.indexOf(t.x)),s=this.annoCtx.helpers.getStringX(t.x),null===t.y&&(l=a.globals.series[t.seriesIndex][h])}else s=(t.x-a.globals.minX)/(a.globals.xRange/a.globals.gridWidth);a.config.yaxis[t.yAxisIndex].logarithmic?o=(l=new y(this.annoCtx.ctx).getLogVal(l,t.yAxisIndex))/a.globals.yLogRatio[t.yAxisIndex]:o=(l-a.globals.minYArr[t.yAxisIndex])/(a.globals.yRange[t.yAxisIndex]/a.globals.gridHeight);if(r=a.globals.gridHeight-o-parseFloat(t.label.style.fontSize)-t.marker.size,n=a.globals.gridHeight-o,a.config.yaxis[t.yAxisIndex]&&a.config.yaxis[t.yAxisIndex].reversed&&(r=o+parseFloat(t.label.style.fontSize)+t.marker.size,n=o),f.isNumber(s)){var c={pSize:t.marker.size,pWidth:t.marker.strokeWidth,pointFillColor:t.marker.fillColor,pointStrokeColor:t.marker.strokeColor,shape:t.marker.shape,pRadius:t.marker.radius,class:"apexcharts-point-annotation-marker ".concat(t.marker.cssClass," ").concat(t.id?t.id:"")},d=this.annoCtx.graphics.drawMarker(s+t.marker.offsetX,n+t.marker.offsetY,c);e.appendChild(d.node);var g=t.label.text?t.label.text:"",u=this.annoCtx.graphics.drawText({x:s+t.label.offsetX,y:r+t.label.offsetY,text:g,textAnchor:t.label.textAnchor,fontSize:t.label.style.fontSize,fontFamily:t.label.style.fontFamily,fontWeight:t.label.style.fontWeight,foreColor:t.label.style.color,cssClass:"apexcharts-point-annotation-label ".concat(t.label.style.cssClass," ").concat(t.id?t.id:"")});if(u.attr({rel:i}),e.appendChild(u.node),t.customSVG.SVG){var p=this.annoCtx.graphics.group({class:"apexcharts-point-annotations-custom-svg "+t.customSVG.cssClass});p.attr({transform:"translate(".concat(s+t.customSVG.offsetX,", ").concat(r+t.customSVG.offsetY,")")}),p.node.innerHTML=t.customSVG.SVG,e.appendChild(p.node)}if(t.image.path){var x=t.image.width?t.image.width:20,b=t.image.height?t.image.height:20;this.annoCtx.addImage({x:s+t.image.offsetX-x/2,y:r+t.image.offsetY-b/2,width:x,height:b,path:t.image.path,appendTo:".apexcharts-point-annotations"})}}}},{key:"drawPointAnnotations",value:function(){var t=this,e=this.w,i=this.annoCtx.graphics.group({class:"apexcharts-point-annotations"});return e.config.annotations.points.map((function(e,a){t.addPointAnnotation(e,i.node,a)})),i}}]),t}();var A,S,C={name:"en",options:{months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],toolbar:{exportToSVG:"Download SVG",exportToPNG:"Download PNG",exportToCSV:"Download CSV",menu:"Menu",selection:"Selection",selectionZoom:"Selection Zoom",zoomIn:"Zoom In",zoomOut:"Zoom Out",pan:"Panning",reset:"Reset Zoom"}}},L=function(){function t(){e(this,t),this.yAxis={show:!0,showAlways:!1,showForNullSeries:!0,seriesName:void 0,opposite:!1,reversed:!1,logarithmic:!1,tickAmount:void 0,forceNiceScale:!1,max:void 0,min:void 0,floating:!1,decimalsInFloat:void 0,labels:{show:!0,minWidth:0,maxWidth:160,offsetX:0,offsetY:0,align:void 0,rotate:0,padding:20,style:{colors:[],fontSize:"11px",fontWeight:400,fontFamily:void 0,cssClass:""},formatter:void 0},axisBorder:{show:!1,color:"#e0e0e0",width:1,offsetX:0,offsetY:0},axisTicks:{show:!1,color:"#e0e0e0",width:6,offsetX:0,offsetY:0},title:{text:void 0,rotate:90,offsetY:0,offsetX:0,style:{color:void 0,fontSize:"11px",fontWeight:900,fontFamily:void 0,cssClass:""}},tooltip:{enabled:!1,offsetX:0},crosshairs:{show:!0,position:"front",stroke:{color:"#b6b6b6",width:1,dashArray:0}}},this.pointAnnotation={x:0,y:null,yAxisIndex:0,seriesIndex:0,marker:{size:4,fillColor:"#fff",strokeWidth:2,strokeColor:"#333",shape:"circle",offsetX:0,offsetY:0,radius:2,cssClass:""},label:{borderColor:"#c2c2c2",borderWidth:1,borderRadius:2,text:void 0,textAnchor:"middle",offsetX:0,offsetY:0,style:{background:"#fff",color:void 0,fontSize:"11px",fontFamily:void 0,fontWeight:400,cssClass:"",padding:{left:5,right:5,top:2,bottom:2}}},customSVG:{SVG:void 0,cssClass:void 0,offsetX:0,offsetY:0},image:{path:void 0,width:20,height:20,offsetX:0,offsetY:0}},this.yAxisAnnotation={y:0,y2:null,strokeDashArray:1,fillColor:"#c2c2c2",borderColor:"#c2c2c2",borderWidth:1,opacity:.3,offsetX:0,offsetY:0,yAxisIndex:0,label:{borderColor:"#c2c2c2",borderWidth:1,borderRadius:2,text:void 0,textAnchor:"end",position:"right",offsetX:0,offsetY:-3,style:{background:"#fff",color:void 0,fontSize:"11px",fontFamily:void 0,fontWeight:400,cssClass:"",padding:{left:5,right:5,top:2,bottom:2}}}},this.xAxisAnnotation={x:0,x2:null,strokeDashArray:1,fillColor:"#c2c2c2",borderColor:"#c2c2c2",borderWidth:1,opacity:.3,offsetX:0,offsetY:0,label:{borderColor:"#c2c2c2",borderWidth:1,borderRadius:2,text:void 0,textAnchor:"middle",orientation:"vertical",position:"top",offsetX:0,offsetY:0,style:{background:"#fff",color:void 0,fontSize:"11px",fontFamily:void 0,fontWeight:400,cssClass:"",padding:{left:5,right:5,top:2,bottom:2}}}},this.text={x:0,y:0,text:"",textAnchor:"start",foreColor:void 0,fontSize:"13px",fontFamily:void 0,fontWeight:400,appendTo:".apexcharts-annotations",backgroundColor:"transparent",borderColor:"#c2c2c2",borderRadius:0,borderWidth:0,paddingLeft:4,paddingRight:4,paddingTop:2,paddingBottom:2},this.shape={x:0,y:0,type:"rect",width:"100%",height:50,appendTo:".apexcharts-annotations",backgroundColor:"#fff",opacity:1,borderWidth:0,borderRadius:4,borderColor:"#c2c2c2"}}return a(t,[{key:"init",value:function(){return{annotations:{position:"front",yaxis:[this.yAxisAnnotation],xaxis:[this.xAxisAnnotation],points:[this.pointAnnotation],texts:[],images:[],shapes:[]},chart:{animations:{enabled:!0,easing:"easeinout",speed:800,animateGradually:{delay:150,enabled:!0},dynamicAnimation:{enabled:!0,speed:350}},background:"transparent",locales:[C],defaultLocale:"en",dropShadow:{enabled:!1,enabledOnSeries:void 0,top:2,left:2,blur:4,color:"#000",opacity:.35},events:{animationEnd:void 0,beforeMount:void 0,mounted:void 0,updated:void 0,click:void 0,mouseMove:void 0,legendClick:void 0,markerClick:void 0,selection:void 0,dataPointSelection:void 0,dataPointMouseEnter:void 0,dataPointMouseLeave:void 0,beforeZoom:void 0,zoomed:void 0,scrolled:void 0},foreColor:"#373d3f",fontFamily:"Helvetica, Arial, sans-serif",height:"auto",parentHeightOffset:15,redrawOnParentResize:!0,id:void 0,group:void 0,offsetX:0,offsetY:0,selection:{enabled:!1,type:"x",fill:{color:"#24292e",opacity:.1},stroke:{width:1,color:"#24292e",opacity:.4,dashArray:3},xaxis:{min:void 0,max:void 0},yaxis:{min:void 0,max:void 0}},sparkline:{enabled:!1},brush:{enabled:!1,autoScaleYaxis:!0,target:void 0},stacked:!1,stackType:"normal",toolbar:{show:!0,offsetX:0,offsetY:0,tools:{download:!0,selection:!0,zoom:!0,zoomin:!0,zoomout:!0,pan:!0,reset:!0,customIcons:[]},export:{csv:{filename:void 0,columnDelimiter:",",headerCategory:"category",headerValue:"value",dateFormatter:function(t){return new Date(t).toDateString()}}},autoSelected:"zoom"},type:"line",width:"100%",zoom:{enabled:!0,type:"x",autoScaleYaxis:!1,zoomedArea:{fill:{color:"#90CAF9",opacity:.4},stroke:{color:"#0D47A1",opacity:.4,width:1}}}},plotOptions:{area:{fillTo:"origin"},bar:{horizontal:!1,columnWidth:"70%",barHeight:"70%",distributed:!1,startingShape:"flat",endingShape:"flat",rangeBarOverlap:!0,colors:{ranges:[],backgroundBarColors:[],backgroundBarOpacity:1,backgroundBarRadius:0},dataLabels:{position:"top",maxItems:100,hideOverflowingLabels:!0,orientation:"horizontal"}},bubble:{minBubbleRadius:void 0,maxBubbleRadius:void 0},candlestick:{colors:{upward:"#00B746",downward:"#EF403C"},wick:{useFillColor:!0}},heatmap:{radius:2,enableShades:!0,shadeIntensity:.5,reverseNegativeShade:!1,distributed:!1,useFillColorAsStroke:!1,colorScale:{inverse:!1,ranges:[],min:void 0,max:void 0}},radialBar:{inverseOrder:!1,startAngle:0,endAngle:360,offsetX:0,offsetY:0,hollow:{margin:5,size:"50%",background:"transparent",image:void 0,imageWidth:150,imageHeight:150,imageOffsetX:0,imageOffsetY:0,imageClipped:!0,position:"front",dropShadow:{enabled:!1,top:0,left:0,blur:3,color:"#000",opacity:.5}},track:{show:!0,startAngle:void 0,endAngle:void 0,background:"#f2f2f2",strokeWidth:"97%",opacity:1,margin:5,dropShadow:{enabled:!1,top:0,left:0,blur:3,color:"#000",opacity:.5}},dataLabels:{show:!0,name:{show:!0,fontSize:"16px",fontFamily:void 0,fontWeight:600,color:void 0,offsetY:0,formatter:function(t){return t}},value:{show:!0,fontSize:"14px",fontFamily:void 0,fontWeight:400,color:void 0,offsetY:16,formatter:function(t){return t+"%"}},total:{show:!1,label:"Total",fontSize:"16px",fontWeight:600,fontFamily:void 0,color:void 0,formatter:function(t){return t.globals.seriesTotals.reduce((function(t,e){return t+e}),0)/t.globals.series.length+"%"}}}},pie:{customScale:1,offsetX:0,offsetY:0,startAngle:0,expandOnClick:!0,dataLabels:{offset:0,minAngleToShowLabel:10},donut:{size:"65%",background:"transparent",labels:{show:!1,name:{show:!0,fontSize:"16px",fontFamily:void 0,fontWeight:600,color:void 0,offsetY:-10,formatter:function(t){return t}},value:{show:!0,fontSize:"20px",fontFamily:void 0,fontWeight:400,color:void 0,offsetY:10,formatter:function(t){return t}},total:{show:!1,showAlways:!1,label:"Total",fontSize:"16px",fontWeight:400,fontFamily:void 0,color:void 0,formatter:function(t){return t.globals.seriesTotals.reduce((function(t,e){return t+e}),0)}}}}},polarArea:{rings:{strokeWidth:1,strokeColor:"#e8e8e8"}},radar:{size:void 0,offsetX:0,offsetY:0,polygons:{strokeWidth:1,strokeColors:"#e8e8e8",connectorColors:"#e8e8e8",fill:{colors:void 0}}}},colors:void 0,dataLabels:{enabled:!0,enabledOnSeries:void 0,formatter:function(t){return null!==t?t:""},textAnchor:"middle",distributed:!1,offsetX:0,offsetY:0,style:{fontSize:"12px",fontFamily:void 0,fontWeight:600,colors:void 0},background:{enabled:!0,foreColor:"#fff",borderRadius:2,padding:4,opacity:.9,borderWidth:1,borderColor:"#fff",dropShadow:{enabled:!1,top:1,left:1,blur:1,color:"#000",opacity:.45}},dropShadow:{enabled:!1,top:1,left:1,blur:1,color:"#000",opacity:.45}},fill:{type:"solid",colors:void 0,opacity:.85,gradient:{shade:"dark",type:"horizontal",shadeIntensity:.5,gradientToColors:void 0,inverseColors:!0,opacityFrom:1,opacityTo:1,stops:[0,50,100],colorStops:[]},image:{src:[],width:void 0,height:void 0},pattern:{style:"squares",width:6,height:6,strokeWidth:2}},grid:{show:!0,borderColor:"#e0e0e0",strokeDashArray:0,position:"back",xaxis:{lines:{show:!1}},yaxis:{lines:{show:!0}},row:{colors:void 0,opacity:.5},column:{colors:void 0,opacity:.5},padding:{top:0,right:10,bottom:0,left:12}},labels:[],legend:{show:!0,showForSingleSeries:!1,showForNullSeries:!0,showForZeroSeries:!0,floating:!1,position:"bottom",horizontalAlign:"center",inverseOrder:!1,fontSize:"12px",fontFamily:void 0,fontWeight:400,width:void 0,height:void 0,formatter:void 0,tooltipHoverFormatter:void 0,offsetX:-20,offsetY:4,labels:{colors:void 0,useSeriesColors:!1},markers:{width:12,height:12,strokeWidth:0,fillColors:void 0,strokeColor:"#fff",radius:12,customHTML:void 0,offsetX:0,offsetY:0,onClick:void 0},itemMargin:{horizontal:5,vertical:2},onItemClick:{toggleDataSeries:!0},onItemHover:{highlightDataSeries:!0}},markers:{discrete:[],size:0,colors:void 0,strokeColors:"#fff",strokeWidth:2,strokeOpacity:.9,strokeDashArray:0,fillOpacity:1,shape:"circle",radius:2,offsetX:0,offsetY:0,onClick:void 0,onDblClick:void 0,showNullDataPoints:!0,hover:{size:void 0,sizeOffset:3}},noData:{text:void 0,align:"center",verticalAlign:"middle",offsetX:0,offsetY:0,style:{color:void 0,fontSize:"14px",fontFamily:void 0}},responsive:[],series:void 0,states:{normal:{filter:{type:"none",value:0}},hover:{filter:{type:"lighten",value:.15}},active:{allowMultipleDataPointsSelection:!1,filter:{type:"darken",value:.65}}},title:{text:void 0,align:"left",margin:5,offsetX:0,offsetY:0,floating:!1,style:{fontSize:"14px",fontWeight:900,fontFamily:void 0,color:void 0}},subtitle:{text:void 0,align:"left",margin:5,offsetX:0,offsetY:30,floating:!1,style:{fontSize:"12px",fontWeight:400,fontFamily:void 0,color:void 0}},stroke:{show:!0,curve:"smooth",lineCap:"butt",width:2,colors:void 0,dashArray:0},tooltip:{enabled:!0,enabledOnSeries:void 0,shared:!0,followCursor:!1,intersect:!1,inverseOrder:!1,custom:void 0,fillSeriesColor:!1,theme:"light",style:{fontSize:"12px",fontFamily:void 0},onDatasetHover:{highlightDataSeries:!1},x:{show:!0,format:"dd MMM",formatter:void 0},y:{formatter:void 0,title:{formatter:function(t){return t+": "}}},z:{formatter:void 0,title:"Size: "},marker:{show:!0,fillColors:void 0},items:{display:"flex"},fixed:{enabled:!1,position:"topRight",offsetX:0,offsetY:0}},xaxis:{type:"category",categories:[],convertedCatToNumeric:!1,sorted:!1,offsetX:0,offsetY:0,labels:{show:!0,rotate:-45,rotateAlways:!1,hideOverlappingLabels:!0,trim:!1,minHeight:void 0,maxHeight:120,showDuplicates:!0,style:{colors:[],fontSize:"12px",fontWeight:400,fontFamily:void 0,cssClass:""},offsetX:0,offsetY:0,format:void 0,formatter:void 0,datetimeUTC:!0,datetimeFormatter:{year:"yyyy",month:"MMM 'yy",day:"dd MMM",hour:"HH:mm",minute:"HH:mm:ss"}},axisBorder:{show:!0,color:"#e0e0e0",width:"100%",height:1,offsetX:0,offsetY:0},axisTicks:{show:!0,color:"#e0e0e0",height:6,offsetX:0,offsetY:0},tickAmount:void 0,tickPlacement:"on",min:void 0,max:void 0,range:void 0,floating:!1,position:"bottom",title:{text:void 0,offsetX:0,offsetY:0,style:{color:void 0,fontSize:"12px",fontWeight:900,fontFamily:void 0,cssClass:""}},crosshairs:{show:!0,width:1,position:"back",opacity:.9,stroke:{color:"#b6b6b6",width:1,dashArray:3},fill:{type:"solid",color:"#B1B9C4",gradient:{colorFrom:"#D8E3F0",colorTo:"#BED1E6",stops:[0,100],opacityFrom:.4,opacityTo:.5}},dropShadow:{enabled:!1,left:0,top:0,blur:1,opacity:.4}},tooltip:{enabled:!0,offsetY:0,formatter:void 0,style:{fontSize:"12px",fontFamily:void 0}}},yaxis:this.yAxis,theme:{mode:"light",palette:"palette1",monochrome:{enabled:!1,color:"#008FFB",shadeTo:"light",shadeIntensity:.65}}}}}]),t}(),P=function(){function t(i){e(this,t),this.ctx=i,this.w=i.w,this.graphics=new b(this.ctx),this.w.globals.isBarHorizontal&&(this.invertAxis=!0),this.helpers=new m(this),this.xAxisAnnotations=new v(this),this.yAxisAnnotations=new w(this),this.pointsAnnotations=new k(this),this.w.globals.isBarHorizontal&&this.w.config.yaxis[0].reversed&&(this.inversedReversedAxis=!0),this.xDivision=this.w.globals.gridWidth/this.w.globals.dataPoints}return a(t,[{key:"drawAxesAnnotations",value:function(){var t=this.w;if(t.globals.axisCharts){for(var e=this.yAxisAnnotations.drawYAxisAnnotations(),i=this.xAxisAnnotations.drawXAxisAnnotations(),a=this.pointsAnnotations.drawPointAnnotations(),s=t.config.chart.animations.enabled,r=[e,i,a],n=[i.node,e.node,a.node],o=0;o<3;o++)t.globals.dom.elGraphical.add(r[o]),!s||t.globals.resized||t.globals.dataChanged||"scatter"!==t.config.chart.type&&"bubble"!==t.config.chart.type&&t.globals.dataPoints>1&&n[o].classList.add("apexcharts-element-hidden"),t.globals.delayedElements.push({el:n[o],index:0});this.helpers.annotationsBackground()}}},{key:"drawShapeAnnos",value:function(){var t=this;this.w.config.annotations.shapes.map((function(e,i){t.addShape(e,i)}))}},{key:"drawImageAnnos",value:function(){var t=this;this.w.config.annotations.images.map((function(e,i){t.addImage(e,i)}))}},{key:"drawTextAnnos",value:function(){var t=this;this.w.config.annotations.texts.map((function(e,i){t.addText(e,i)}))}},{key:"addXaxisAnnotation",value:function(t,e,i){this.xAxisAnnotations.addXaxisAnnotation(t,e,i)}},{key:"addYaxisAnnotation",value:function(t,e,i){this.yAxisAnnotations.addYaxisAnnotation(t,e,i)}},{key:"addPointAnnotation",value:function(t,e,i){this.pointsAnnotations.addPointAnnotation(t,e,i)}},{key:"addText",value:function(t,e){var i=t.x,a=t.y,s=t.text,r=t.textAnchor,n=t.foreColor,o=t.fontSize,l=t.fontFamily,h=t.fontWeight,c=t.cssClass,d=t.backgroundColor,g=t.borderWidth,u=t.strokeDashArray,f=t.borderRadius,p=t.borderColor,x=t.appendTo,b=void 0===x?".apexcharts-annotations":x,m=t.paddingLeft,v=void 0===m?4:m,y=t.paddingRight,w=void 0===y?4:y,k=t.paddingBottom,A=void 0===k?2:k,S=t.paddingTop,C=void 0===S?2:S,L=this.w,P=this.graphics.drawText({x:i,y:a,text:s,textAnchor:r||"start",fontSize:o||"12px",fontWeight:h||"regular",fontFamily:l||L.config.chart.fontFamily,foreColor:n||L.config.chart.foreColor,cssClass:c}),T=L.globals.dom.baseEl.querySelector(b);T&&T.appendChild(P.node);var z=P.bbox();if(t.draggable&&this.helpers.makeAnnotationDraggable(P,"texts",e),s){var I=this.graphics.drawRect(z.x-v,z.y-C,z.width+v+w,z.height+A+C,f,d||"transparent",1,g,p,u);T.insertBefore(I.node,P.node)}}},{key:"addShape",value:function(t,e){var i={type:t.type,x:t.x||0,y:t.y||0,x1:t.x1||0,y1:t.y1||0,lineColor:t.lineColor||"#a8a8a8",dashArray:t.dashArray||0,strokeWidth:t.strokeWidth||null,width:t.width||"100%",height:t.height||50,circleRadius:t.radius||25,backgroundColor:t.backgroundColor||"#fff",opacity:t.opacity||1,borderWidth:t.borderWidth||0,borderRadius:t.borderRadius||4,borderColor:t.borderColor||"#c2c2c2",appendTo:t.appendTo||".apexcharts-annotations"},a=this.w;String(i.width).indexOf("%")>-1&&(i.width=parseInt(i.width,10)*parseInt(a.globals.svgWidth,10)/100);var s=null;switch(i.type){case"circle":s=this.graphics.drawCircle(i.circleRadius,{fill:i.backgroundColor,stroke:i.borderColor,"stroke-width":i.borderWidth,opacity:i.opacity,cx:i.x,cy:i.y});break;case"line":s=this.graphics.drawLine(i.x,i.y,i.x1,i.y1,i.lineColor,i.dashArray,i.strokeWidth);break;default:s=this.graphics.drawRect(i.x,i.y,i.width,i.height,i.borderRadius,i.backgroundColor,i.opacity,i.borderWidth,i.borderColor)}var r=a.globals.dom.baseEl.querySelector(i.appendTo);r&&r.appendChild(s.node),t.draggable&&(this.helpers.makeAnnotationDraggable(s,"shapes",e),s.node.classList.add("apexcharts-resizable-element"))}},{key:"addImage",value:function(t,e){var i=this.w,a=t.path,s=t.x,r=void 0===s?0:s,n=t.y,o=void 0===n?0:n,l=t.width,h=void 0===l?20:l,c=t.height,d=void 0===c?20:c,g=t.appendTo,u=void 0===g?".apexcharts-annotations":g,f=i.globals.dom.Paper.image(a);f.size(h,d).move(r,o);var p=i.globals.dom.baseEl.querySelector(u);p&&p.appendChild(f.node),t.draggable&&(this.helpers.makeAnnotationDraggable(f,"images",e),f.node.classList.add("apexcharts-resizable-element"))}},{key:"addXaxisAnnotationExternal",value:function(t,e,i){return this.addAnnotationExternal({params:t,pushToMemory:e,context:i,type:"xaxis",contextMethod:i.addXaxisAnnotation}),i}},{key:"addYaxisAnnotationExternal",value:function(t,e,i){return this.addAnnotationExternal({params:t,pushToMemory:e,context:i,type:"yaxis",contextMethod:i.addYaxisAnnotation}),i}},{key:"addPointAnnotationExternal",value:function(t,e,i){return void 0===this.invertAxis&&(this.invertAxis=i.w.globals.isBarHorizontal),this.addAnnotationExternal({params:t,pushToMemory:e,context:i,type:"point",contextMethod:i.addPointAnnotation}),i}},{key:"addAnnotationExternal",value:function(t){var e=t.params,i=t.pushToMemory,a=t.context,s=t.type,r=t.contextMethod,n=a,o=n.w,l=o.globals.dom.baseEl.querySelector(".apexcharts-".concat(s,"-annotations")),h=l.childNodes.length+1,c=new L,d=Object.assign({},"xaxis"===s?c.xAxisAnnotation:"yaxis"===s?c.yAxisAnnotation:c.pointAnnotation),g=f.extend(d,e);switch(s){case"xaxis":this.addXaxisAnnotation(g,l,h);break;case"yaxis":this.addYaxisAnnotation(g,l,h);break;case"point":this.addPointAnnotation(g,l,h)}var u=o.globals.dom.baseEl.querySelector(".apexcharts-".concat(s,"-annotations .apexcharts-").concat(s,"-annotation-label[rel='").concat(h,"']")),p=this.helpers.addBackgroundToAnno(u,g);return p&&l.insertBefore(p.node,u),i&&o.globals.memory.methodsToExec.push({context:n,id:g.id?g.id:f.randomId(),method:r,label:"addAnnotation",params:e}),a}},{key:"clearAnnotations",value:function(t){var e=t.w,i=e.globals.dom.baseEl.querySelectorAll(".apexcharts-yaxis-annotations, .apexcharts-xaxis-annotations, .apexcharts-point-annotations");e.globals.memory.methodsToExec.map((function(t,i){"addText"!==t.label&&"addAnnotation"!==t.label||e.globals.memory.methodsToExec.splice(i,1)})),i=f.listToArray(i),Array.prototype.forEach.call(i,(function(t){for(;t.firstChild;)t.removeChild(t.firstChild)}))}},{key:"removeAnnotation",value:function(t,e){var i=t.w,a=i.globals.dom.baseEl.querySelectorAll(".".concat(e));a&&(i.globals.memory.methodsToExec.map((function(t,a){t.id===e&&i.globals.memory.methodsToExec.splice(a,1)})),Array.prototype.forEach.call(a,(function(t){t.parentElement.removeChild(t)})))}}]),t}(),T=function(){function t(i){e(this,t),this.ctx=i,this.w=i.w,this.opts=null,this.seriesIndex=0}return a(t,[{key:"clippedImgArea",value:function(t){var e=this.w,i=e.config,a=parseInt(e.globals.gridWidth,10),s=parseInt(e.globals.gridHeight,10),r=a>s?a:s,n=t.image,o=0,l=0;void 0===t.width&&void 0===t.height?void 0!==i.fill.image.width&&void 0!==i.fill.image.height?(o=i.fill.image.width+1,l=i.fill.image.height):(o=r+1,l=r):(o=t.width,l=t.height);var h=document.createElementNS(e.globals.SVGNS,"pattern");b.setAttrs(h,{id:t.patternID,patternUnits:t.patternUnits?t.patternUnits:"userSpaceOnUse",width:o+"px",height:l+"px"});var c=document.createElementNS(e.globals.SVGNS,"image");h.appendChild(c),c.setAttributeNS(window.SVG.xlink,"href",n),b.setAttrs(c,{x:0,y:0,preserveAspectRatio:"none",width:o+"px",height:l+"px"}),c.style.opacity=t.opacity,e.globals.dom.elDefs.node.appendChild(h)}},{key:"getSeriesIndex",value:function(t){var e=this.w;return("bar"===e.config.chart.type||"rangeBar"===e.config.chart.type)&&e.config.plotOptions.bar.distributed||"heatmap"===e.config.chart.type?this.seriesIndex=t.seriesNumber:this.seriesIndex=t.seriesNumber%e.globals.series.length,this.seriesIndex}},{key:"fillPath",value:function(t){var e=this.w;this.opts=t;var i,a,s,r=this.w.config;this.seriesIndex=this.getSeriesIndex(t);var n=this.getFillColors()[this.seriesIndex];"function"==typeof n&&(n=n({seriesIndex:this.seriesIndex,dataPointIndex:t.dataPointIndex,value:t.value,w:e}));var o=this.getFillType(this.seriesIndex),l=Array.isArray(r.fill.opacity)?r.fill.opacity[this.seriesIndex]:r.fill.opacity,h=n;if(t.color&&(n=t.color),-1===n.indexOf("rgb")?n.length<9&&(h=f.hexToRgba(n,l)):n.indexOf("rgba")>-1&&(l=f.getOpacityFromRGBA(n)),t.opacity&&(l=t.opacity),"pattern"===o&&(a=this.handlePatternFill(a,n,l,h)),"gradient"===o&&(s=this.handleGradientFill(n,l,this.seriesIndex)),"image"===o){var c=r.fill.image.src,d=t.patternID?t.patternID:"";this.clippedImgArea({opacity:l,image:Array.isArray(c)?t.seriesNumber<c.length?c[t.seriesNumber]:c[0]:c,width:t.width?t.width:void 0,height:t.height?t.height:void 0,patternUnits:t.patternUnits,patternID:"pattern".concat(e.globals.cuid).concat(t.seriesNumber+1).concat(d)}),i="url(#pattern".concat(e.globals.cuid).concat(t.seriesNumber+1).concat(d,")")}else i="gradient"===o?s:"pattern"===o?a:h;return t.solid&&(i=h),i}},{key:"getFillType",value:function(t){var e=this.w;return Array.isArray(e.config.fill.type)?e.config.fill.type[t]:e.config.fill.type}},{key:"getFillColors",value:function(){var t=this.w,e=t.config,i=this.opts,a=[];return t.globals.comboCharts?"line"===t.config.series[this.seriesIndex].type?t.globals.stroke.colors instanceof Array?a=t.globals.stroke.colors:a.push(t.globals.stroke.colors):t.globals.fill.colors instanceof Array?a=t.globals.fill.colors:a.push(t.globals.fill.colors):"line"===e.chart.type?t.globals.stroke.colors instanceof Array?a=t.globals.stroke.colors:a.push(t.globals.stroke.colors):t.globals.fill.colors instanceof Array?a=t.globals.fill.colors:a.push(t.globals.fill.colors),void 0!==i.fillColors&&(a=[],i.fillColors instanceof Array?a=i.fillColors.slice():a.push(i.fillColors)),a}},{key:"handlePatternFill",value:function(t,e,i,a){var s=this.w.config,r=this.opts,n=new b(this.ctx),o=void 0===s.fill.pattern.strokeWidth?Array.isArray(s.stroke.width)?s.stroke.width[this.seriesIndex]:s.stroke.width:Array.isArray(s.fill.pattern.strokeWidth)?s.fill.pattern.strokeWidth[this.seriesIndex]:s.fill.pattern.strokeWidth,l=e;s.fill.pattern.style instanceof Array?t=void 0!==s.fill.pattern.style[r.seriesNumber]?n.drawPattern(s.fill.pattern.style[r.seriesNumber],s.fill.pattern.width,s.fill.pattern.height,l,o,i):a:t=n.drawPattern(s.fill.pattern.style,s.fill.pattern.width,s.fill.pattern.height,l,o,i);return t}},{key:"handleGradientFill",value:function(t,e,i){var a,s=this.w.config,r=this.opts,n=new b(this.ctx),o=new f,l=s.fill.gradient.type,h=t,c=void 0===s.fill.gradient.opacityFrom?e:Array.isArray(s.fill.gradient.opacityFrom)?s.fill.gradient.opacityFrom[i]:s.fill.gradient.opacityFrom;h.indexOf("rgba")>-1&&(c=f.getOpacityFromRGBA(h));var d=void 0===s.fill.gradient.opacityTo?e:Array.isArray(s.fill.gradient.opacityTo)?s.fill.gradient.opacityTo[i]:s.fill.gradient.opacityTo;if(void 0===s.fill.gradient.gradientToColors||0===s.fill.gradient.gradientToColors.length)a="dark"===s.fill.gradient.shade?o.shadeColor(-1*parseFloat(s.fill.gradient.shadeIntensity),t.indexOf("rgb")>-1?f.rgb2hex(t):t):o.shadeColor(parseFloat(s.fill.gradient.shadeIntensity),t.indexOf("rgb")>-1?f.rgb2hex(t):t);else{var g=s.fill.gradient.gradientToColors[r.seriesNumber];a=g,g.indexOf("rgba")>-1&&(d=f.getOpacityFromRGBA(g))}if(s.fill.gradient.inverseColors){var u=h;h=a,a=u}return h.indexOf("rgb")>-1&&(h=f.rgb2hex(h)),a.indexOf("rgb")>-1&&(a=f.rgb2hex(a)),n.drawGradient(l,h,a,c,d,r.size,s.fill.gradient.stops,s.fill.gradient.colorStops,i)}}]),t}(),z=function(){function t(i,a){e(this,t),this.ctx=i,this.w=i.w}return a(t,[{key:"setGlobalMarkerSize",value:function(){var t=this.w;if(t.globals.markers.size=Array.isArray(t.config.markers.size)?t.config.markers.size:[t.config.markers.size],t.globals.markers.size.length>0){if(t.globals.markers.size.length<t.globals.series.length+1)for(var e=0;e<=t.globals.series.length;e++)void 0===t.globals.markers.size[e]&&t.globals.markers.size.push(t.globals.markers.size[0])}else t.globals.markers.size=t.config.series.map((function(e){return t.config.markers.size}))}},{key:"plotChartMarkers",value:function(t,e,i,a){var s,r=arguments.length>4&&void 0!==arguments[4]&&arguments[4],n=this.w,o=e,l=t,h=null,c=new b(this.ctx);if((n.globals.markers.size[e]>0||r)&&(h=c.group({class:r?"":"apexcharts-series-markers"})).attr("clip-path","url(#gridRectMarkerMask".concat(n.globals.cuid,")")),l.x instanceof Array)for(var d=0;d<l.x.length;d++){var g=i;1===i&&0===d&&(g=0),1===i&&1===d&&(g=1);var u="apexcharts-marker";"line"!==n.config.chart.type&&"area"!==n.config.chart.type||n.globals.comboCharts||n.config.tooltip.intersect||(u+=" no-pointer-events");var x=Array.isArray(n.config.markers.size)?n.globals.markers.size[e]>0:n.config.markers.size>0;if(x||r){f.isNumber(l.y[d])?u+=" w".concat(f.randomId()):u="apexcharts-nullpoint";var m=this.getMarkerConfig(u,e,g);n.config.series[o].data[i]&&(n.config.series[o].data[i].fillColor&&(m.pointFillColor=n.config.series[o].data[i].fillColor),n.config.series[o].data[i].strokeColor&&(m.pointStrokeColor=n.config.series[o].data[i].strokeColor)),a&&(m.pSize=a),(s=c.drawMarker(l.x[d],l.y[d],m)).attr("rel",g),s.attr("j",g),s.attr("index",e),s.node.setAttribute("default-marker-size",m.pSize);var v=new p(this.ctx);v.setSelectionFilter(s,e,g),this.addEvents(s),h&&h.add(s)}else void 0===n.globals.pointsArray[e]&&(n.globals.pointsArray[e]=[]),n.globals.pointsArray[e].push([l.x[d],l.y[d]])}return h}},{key:"getMarkerConfig",value:function(t,e){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null,a=this.w,s=this.getMarkerStyle(e),r=a.globals.markers.size[e],n=a.config.markers;return null!==i&&n.discrete.length&&n.discrete.map((function(t){t.seriesIndex===e&&t.dataPointIndex===i&&(s.pointStrokeColor=t.strokeColor,s.pointFillColor=t.fillColor,r=t.size)})),{pSize:r,pRadius:n.radius,pWidth:n.strokeWidth instanceof Array?n.strokeWidth[e]:n.strokeWidth,pointStrokeColor:s.pointStrokeColor,pointFillColor:s.pointFillColor,shape:n.shape instanceof Array?n.shape[e]:n.shape,class:t,pointStrokeOpacity:n.strokeOpacity instanceof Array?n.strokeOpacity[e]:n.strokeOpacity,pointStrokeDashArray:n.strokeDashArray instanceof Array?n.strokeDashArray[e]:n.strokeDashArray,pointFillOpacity:n.fillOpacity instanceof Array?n.fillOpacity[e]:n.fillOpacity,seriesIndex:e}}},{key:"addEvents",value:function(t){var e=this.w,i=new b(this.ctx);t.node.addEventListener("mouseenter",i.pathMouseEnter.bind(this.ctx,t)),t.node.addEventListener("mouseleave",i.pathMouseLeave.bind(this.ctx,t)),t.node.addEventListener("mousedown",i.pathMouseDown.bind(this.ctx,t)),t.node.addEventListener("click",e.config.markers.onClick),t.node.addEventListener("dblclick",e.config.markers.onDblClick),t.node.addEventListener("touchstart",i.pathMouseDown.bind(this.ctx,t),{passive:!0})}},{key:"getMarkerStyle",value:function(t){var e=this.w,i=e.globals.markers.colors,a=e.config.markers.strokeColor||e.config.markers.strokeColors;return{pointStrokeColor:a instanceof Array?a[t]:a,pointFillColor:i instanceof Array?i[t]:i}}}]),t}(),I=function(){function t(i){e(this,t),this.ctx=i,this.w=i.w,this.initialAnim=this.w.config.chart.animations.enabled,this.dynamicAnim=this.initialAnim&&this.w.config.chart.animations.dynamicAnimation.enabled}return a(t,[{key:"draw",value:function(t,e,i){var a=this.w,s=new b(this.ctx),r=i.realIndex,n=i.pointsPos,o=i.zRatio,l=i.elParent,h=s.group({class:"apexcharts-series-markers apexcharts-series-".concat(a.config.chart.type)});if(h.attr("clip-path","url(#gridRectMarkerMask".concat(a.globals.cuid,")")),n.x instanceof Array)for(var c=0;c<n.x.length;c++){var d=e+1,g=!0;0===e&&0===c&&(d=0),0===e&&1===c&&(d=1);var u=0,f=a.globals.markers.size[r];if(o!==1/0){f=a.globals.seriesZ[r][d]/o;var p=a.config.plotOptions.bubble;p.minBubbleRadius&&f<p.minBubbleRadius&&(f=p.minBubbleRadius),p.maxBubbleRadius&&f>p.maxBubbleRadius&&(f=p.maxBubbleRadius)}a.config.chart.animations.enabled||(u=f);var x=n.x[c],m=n.y[c];if(u=u||0,null!==m&&void 0!==a.globals.series[r][d]||(g=!1),g){var v=this.drawPoint(x,m,u,f,r,d,e);h.add(v)}l.add(h)}}},{key:"drawPoint",value:function(t,e,i,a,s,r,n){var o=this.w,l=s,h=new x(this.ctx),c=new p(this.ctx),d=new T(this.ctx),g=new z(this.ctx),u=new b(this.ctx),f=g.getMarkerConfig("apexcharts-marker",l),m=d.fillPath({seriesNumber:s,dataPointIndex:r,patternUnits:"objectBoundingBox",value:o.globals.series[s][n]}),v=u.drawCircle(i);if(o.config.series[l].data[r]&&o.config.series[l].data[r].fillColor&&(m=o.config.series[l].data[r].fillColor),v.attr({cx:t,cy:e,fill:m,stroke:f.pointStrokeColor,r:a,"stroke-width":f.pWidth,"stroke-dasharray":f.pointStrokeDashArray,"stroke-opacity":f.pointStrokeOpacity}),o.config.chart.dropShadow.enabled){var y=o.config.chart.dropShadow;c.dropShadow(v,y,s)}if(this.initialAnim&&!o.globals.dataChanged&&!o.globals.resized){var w=o.config.chart.animations.speed;h.animateCircleRadius(v,0,a,w,o.globals.easing,(function(){window.setTimeout((function(){h.animationCompleted(v)}),100)}))}if(o.globals.dataChanged)if(this.dynamicAnim){var k,A,S,C,L=o.config.chart.animations.dynamicAnimation.speed;null!=(C=o.globals.previousPaths[s]&&o.globals.previousPaths[s][n])&&(k=C.x,A=C.y,S=void 0!==C.r?C.r:a);for(var P=0;P<o.globals.collapsedSeries.length;P++)o.globals.collapsedSeries[P].index===s&&(L=1,a=0);0===t&&0===e&&(a=0),h.animateCircle(v,{cx:k,cy:A,r:S},{cx:t,cy:e,r:a},L,o.globals.easing)}else v.attr({r:a});return v.attr({rel:r,j:r,index:s,"default-marker-size":a}),c.setSelectionFilter(v,s,r),g.addEvents(v),v.node.classList.add("apexcharts-marker"),v}},{key:"centerTextInBubble",value:function(t){var e=this.w;return{y:t+=parseInt(e.config.dataLabels.style.fontSize,10)/4}}}]),t}(),M=function(){function t(i){e(this,t),this.ctx=i,this.w=i.w}return a(t,[{key:"dataLabelsCorrection",value:function(t,e,i,a,s,r,n){var o=this.w,l=!1,h=new b(this.ctx).getTextRects(i,n),c=h.width,d=h.height;void 0===o.globals.dataLabelsRects[a]&&(o.globals.dataLabelsRects[a]=[]),o.globals.dataLabelsRects[a].push({x:t,y:e,width:c,height:d});var g=o.globals.dataLabelsRects[a].length-2,u=void 0!==o.globals.lastDrawnDataLabelsIndexes[a]?o.globals.lastDrawnDataLabelsIndexes[a][o.globals.lastDrawnDataLabelsIndexes[a].length-1]:0;if(void 0!==o.globals.dataLabelsRects[a][g]){var f=o.globals.dataLabelsRects[a][u];(t>f.x+f.width+2||e>f.y+f.height+2||t+c<f.x)&&(l=!0)}return(0===s||r)&&(l=!0),{x:t,y:e,textRects:h,drawnextLabel:l}}},{key:"drawDataLabel",value:function(t,e,i){var a=this,s=arguments.length>4&&void 0!==arguments[4]?arguments[4]:2,r=this.w,n=new b(this.ctx),o=r.config.dataLabels,l=0,h=0,c=i,d=null;if(!o.enabled||t.x instanceof Array!=!0)return d;d=n.group({class:"apexcharts-data-labels"});for(var g=0;g<t.x.length;g++)if(l=t.x[g]+o.offsetX,h=t.y[g]+o.offsetY+s,!isNaN(l)){1===i&&0===g&&(c=0),1===i&&1===g&&(c=1);var u=r.globals.series[e][c],f="",p=function(t){return r.config.dataLabels.formatter(t,{ctx:a.ctx,seriesIndex:e,dataPointIndex:c,w:r})};if("bubble"===r.config.chart.type){f=p(u=r.globals.seriesZ[e][c]),h=t.y[g];var x=new I(this.ctx),m=x.centerTextInBubble(h,e,c);h=m.y}else void 0!==u&&(f=p(u));this.plotDataLabelsText({x:l,y:h,text:f,i:e,j:c,parent:d,offsetCorrection:!0,dataLabelsConfig:r.config.dataLabels})}return d}},{key:"plotDataLabelsText",value:function(t){var e=this.w,i=new b(this.ctx),a=t.x,s=t.y,r=t.i,n=t.j,o=t.text,l=t.textAnchor,h=t.parent,c=t.dataLabelsConfig,d=t.color,g=t.alwaysDrawDataLabel,u=t.offsetCorrection;if(!(Array.isArray(e.config.dataLabels.enabledOnSeries)&&e.config.dataLabels.enabledOnSeries.indexOf(r)<0)){var f={x:a,y:s,drawnextLabel:!0};u&&(f=this.dataLabelsCorrection(a,s,o,r,n,g,parseInt(c.style.fontSize,10))),e.globals.zoomed||(a=f.x,s=f.y),f.textRects&&(a+f.textRects.width<-20||a>e.globals.gridWidth+20)&&(o="");var x=e.globals.dataLabels.style.colors[r];(("bar"===e.config.chart.type||"rangeBar"===e.config.chart.type)&&e.config.plotOptions.bar.distributed||e.config.dataLabels.distributed)&&(x=e.globals.dataLabels.style.colors[n]),d&&(x=d);var m=c.offsetX,v=c.offsetY;if("bar"!==e.config.chart.type&&"rangeBar"!==e.config.chart.type||(m=0,v=0),f.drawnextLabel){var y=i.drawText({width:100,height:parseInt(c.style.fontSize,10),x:a+m,y:s+v,foreColor:x,textAnchor:l||c.textAnchor,text:o,fontSize:c.style.fontSize,fontFamily:c.style.fontFamily,fontWeight:c.style.fontWeight||"normal"});if(y.attr({class:"apexcharts-datalabel",cx:a,cy:s}),c.dropShadow.enabled){var w=c.dropShadow;new p(this.ctx).dropShadow(y,w)}h.add(y),void 0===e.globals.lastDrawnDataLabelsIndexes[r]&&(e.globals.lastDrawnDataLabelsIndexes[r]=[]),e.globals.lastDrawnDataLabelsIndexes[r].push(n)}}}},{key:"addBackgroundToDataLabel",value:function(t,e){var i=this.w,a=i.config.dataLabels.background,s=a.padding,r=a.padding/2,n=e.width,o=e.height,l=new b(this.ctx).drawRect(e.x-s,e.y-r/2,n+2*s,o+r,a.borderRadius,"transparent"===i.config.chart.background?"#fff":i.config.chart.background,a.opacity,a.borderWidth,a.borderColor);a.dropShadow.enabled&&new p(this.ctx).dropShadow(l,a.dropShadow);return l}},{key:"dataLabelsBackground",value:function(){var t=this.w;if("bubble"!==t.config.chart.type)for(var e=t.globals.dom.baseEl.querySelectorAll(".apexcharts-datalabels text"),i=0;i<e.length;i++){var a=e[i],s=a.getBBox(),r=null;if(s.width&&s.height&&(r=this.addBackgroundToDataLabel(a,s)),r){a.parentNode.insertBefore(r.node,a);var n=a.getAttribute("fill");t.config.chart.animations.enabled&&!t.globals.resized&&!t.globals.dataChanged?r.animate().attr({fill:n}):r.attr({fill:n}),a.setAttribute("fill",t.config.dataLabels.background.foreColor)}}}},{key:"bringForward",value:function(){for(var t=this.w,e=t.globals.dom.baseEl.getElementsByClassName("apexcharts-datalabels"),i=t.globals.dom.baseEl.querySelector(".apexcharts-plot-series:last-child"),a=0;a<e.length;a++)i&&i.insertBefore(e[a],i.nextSibling)}}]),t}(),E=function(){function t(i){e(this,t),this.w=i.w,this.barCtx=i}return a(t,[{key:"handleBarDataLabels",value:function(t){var e=t.x,i=t.y,a=t.y1,s=t.y2,r=t.i,n=t.j,o=t.realIndex,l=t.series,h=t.barHeight,c=t.barWidth,d=t.barYPosition,g=t.visibleSeries,u=t.renderedPath,f=this.w,p=new b(this.barCtx.ctx),x=Array.isArray(this.barCtx.strokeWidth)?this.barCtx.strokeWidth[o]:this.barCtx.strokeWidth,m=e+parseFloat(c*g),v=i+parseFloat(h*g);f.globals.isXNumeric&&!f.globals.isBarHorizontal&&(m=e+parseFloat(c*(g+1)),v=i+parseFloat(h*(g+1))-x);var y=e,w=i,k={},A=f.config.dataLabels,S=this.barCtx.barOptions.dataLabels;void 0!==d&&this.barCtx.isTimelineBar&&(v=d,w=d);var C=A.offsetX,L=A.offsetY,P={width:0,height:0};if(f.config.dataLabels.enabled){var T=String(f.globals.minY).length>String(f.globals.maxY).length?f.globals.minY:f.globals.maxY;P=p.getTextRects(f.globals.yLabelFormatters[0](T),parseFloat(A.style.fontSize))}var z={x:e,y:i,i:r,j:n,renderedPath:u,bcx:m,bcy:v,barHeight:h,barWidth:c,textRects:P,strokeWidth:x,dataLabelsX:y,dataLabelsY:w,barDataLabelsConfig:S,offX:C,offY:L};return k=this.barCtx.isHorizontal?this.calculateBarsDataLabelsPosition(z):this.calculateColumnsDataLabelsPosition(z),u.attr({cy:k.bcy,cx:k.bcx,j:n,val:l[r][n],barHeight:h,barWidth:c}),this.drawCalculatedDataLabels({x:k.dataLabelsX,y:k.dataLabelsY,val:this.barCtx.isTimelineBar?[a,s]:l[r][n],i:o,j:n,barWidth:c,barHeight:h,textRects:P,dataLabelsConfig:A})}},{key:"calculateColumnsDataLabelsPosition",value:function(t){var e,i=this.w,a=t.i,s=t.j,r=t.y,n=t.bcx,o=t.barWidth,l=t.barHeight,h=t.textRects,c=t.dataLabelsY,d=t.barDataLabelsConfig,g=t.strokeWidth,u=t.offX,f=t.offY;l=Math.abs(l);var p="vertical"===i.config.plotOptions.bar.dataLabels.orientation;n-=g/2;var x=i.globals.gridWidth/i.globals.dataPoints;if(e=i.globals.isXNumeric?n-o/2+u:n-x+o/2+u,p){e=e+h.height/2-g/2-2}var b=this.barCtx.series[a][s]<0,m=r;switch(this.barCtx.isReversed&&(m=r-l+(b?2*l:0),r-=l),d.position){case"center":c=p?b?m+l/2+f:m+l/2-f:b?m-l/2+h.height/2+f:m+l/2+h.height/2-f;break;case"bottom":c=p?b?m+l+f:m+l-f:b?m-l+h.height+g+f:m+l-h.height/2+g-f;break;case"top":c=p?b?m+f:m-f:b?m-h.height/2-f:m+h.height+f}return i.config.chart.stacked||(c<0?c=0+g:c+h.height/3>i.globals.gridHeight&&(c=i.globals.gridHeight-g)),{bcx:n,bcy:r,dataLabelsX:e,dataLabelsY:c}}},{key:"calculateBarsDataLabelsPosition",value:function(t){var e=this.w,i=t.x,a=t.i,s=t.j,r=t.bcy,n=t.barHeight,o=t.barWidth,l=t.textRects,h=t.dataLabelsX,c=t.strokeWidth,d=t.barDataLabelsConfig,g=t.offX,u=t.offY,f=e.globals.gridHeight/e.globals.dataPoints;o=Math.abs(o);var p=r-(this.barCtx.isTimelineBar?0:f)+n/2+l.height/2+u-3,x=this.barCtx.series[a][s]<0,b=i;switch(this.barCtx.isReversed&&(b=i+o-(x?2*o:0),i=e.globals.gridWidth-o),d.position){case"center":h=x?b+o/2-g:b-o/2+g;break;case"bottom":h=x?b+o-c-Math.round(l.width/2)-g:b-o+c+Math.round(l.width/2)+g;break;case"top":h=x?b-c+Math.round(l.width/2)-g:b-c-Math.round(l.width/2)+g}return e.config.chart.stacked||(h<0?h=h+l.width+c:h+l.width/2>e.globals.gridWidth&&(h=e.globals.gridWidth-l.width-c)),{bcx:i,bcy:r,dataLabelsX:h,dataLabelsY:p}}},{key:"drawCalculatedDataLabels",value:function(t){var e=t.x,i=t.y,a=t.val,s=t.i,r=t.j,o=t.textRects,l=t.barHeight,h=t.barWidth,c=t.dataLabelsConfig,d=this.w,g="rotate(0)";"vertical"===d.config.plotOptions.bar.dataLabels.orientation&&(g="rotate(-90, ".concat(e,", ").concat(i,")"));var u=new M(this.barCtx.ctx),f=new b(this.barCtx.ctx),p=c.formatter,x=null,m=d.globals.collapsedSeriesIndices.indexOf(s)>-1;if(c.enabled&&!m){x=f.group({class:"apexcharts-data-labels",transform:g});var v="";void 0!==a&&(v=p(a,{seriesIndex:s,dataPointIndex:r,w:d})),0===a&&d.config.chart.stacked&&(v="");var y=d.globals.series[s][r]<=0,w=d.config.plotOptions.bar.dataLabels.position;if("vertical"===d.config.plotOptions.bar.dataLabels.orientation&&("top"===w&&(c.textAnchor=y?"end":"start"),"center"===w&&(c.textAnchor="middle"),"bottom"===w&&(c.textAnchor=y?"end":"start")),this.barCtx.isTimelineBar&&this.barCtx.barOptions.dataLabels.hideOverflowingLabels)h<f.getTextRects(v,parseFloat(c.style.fontSize)).width&&(v="");d.config.chart.stacked&&this.barCtx.barOptions.dataLabels.hideOverflowingLabels&&(this.barCtx.isHorizontal?((h=Math.abs(d.globals.series[s][r])/this.barCtx.invertedYRatio[this.barCtx.yaxisIndex])>0&&o.width/1.6>h||h<0&&o.width/1.6<h)&&(v=""):(l=Math.abs(d.globals.series[s][r])/this.barCtx.yRatio[this.barCtx.yaxisIndex],o.height/1.6>l&&(v="")));var k=n({},c);this.barCtx.isHorizontal&&a<0&&("start"===c.textAnchor?k.textAnchor="end":"end"===c.textAnchor&&(k.textAnchor="start")),u.plotDataLabelsText({x:e,y:i,text:v,i:s,j:r,parent:x,dataLabelsConfig:k,alwaysDrawDataLabel:!0,offsetCorrection:!0})}return x}}]),t}(),X=function(){function t(i){e(this,t),this.ctx=i,this.w=i.w,this.legendInactiveClass="legend-mouseover-inactive"}return a(t,[{key:"getAllSeriesEls",value:function(){return this.w.globals.dom.baseEl.getElementsByClassName("apexcharts-series")}},{key:"getSeriesByName",value:function(t){return this.w.globals.dom.baseEl.querySelector("[seriesName='".concat(f.escapeString(t),"']"))}},{key:"isSeriesHidden",value:function(t){var e=this.getSeriesByName(t),i=parseInt(e.getAttribute("data:realIndex"),10);return{isHidden:e.classList.contains("apexcharts-series-collapsed"),realIndex:i}}},{key:"addCollapsedClassToSeries",value:function(t,e){var i=this.w;function a(i){for(var a=0;a<i.length;a++)i[a].index===e&&t.node.classList.add("apexcharts-series-collapsed")}a(i.globals.collapsedSeries),a(i.globals.ancillaryCollapsedSeries)}},{key:"toggleSeries",value:function(t){var e=this.isSeriesHidden(t);return this.ctx.legend.legendHelpers.toggleDataSeries(e.realIndex,e.isHidden),e.isHidden}},{key:"showSeries",value:function(t){var e=this.isSeriesHidden(t);e.isHidden&&this.ctx.legend.legendHelpers.toggleDataSeries(e.realIndex,!0)}},{key:"hideSeries",value:function(t){var e=this.isSeriesHidden(t);e.isHidden||this.ctx.legend.legendHelpers.toggleDataSeries(e.realIndex,!1)}},{key:"resetSeries",value:function(){var t=!(arguments.length>0&&void 0!==arguments[0])||arguments[0],e=!(arguments.length>1&&void 0!==arguments[1])||arguments[1],i=!(arguments.length>2&&void 0!==arguments[2])||arguments[2],a=this.w,s=f.clone(a.globals.initialSeries);a.globals.previousPaths=[],i?(a.globals.collapsedSeries=[],a.globals.ancillaryCollapsedSeries=[],a.globals.collapsedSeriesIndices=[],a.globals.ancillaryCollapsedSeriesIndices=[]):s=this.emptyCollapsedSeries(s),a.config.series=s,t&&(e&&(a.globals.zoomed=!1,this.ctx.updateHelpers.revertDefaultAxisMinMax()),this.ctx.updateHelpers._updateSeries(s,a.config.chart.animations.dynamicAnimation.enabled))}},{key:"emptyCollapsedSeries",value:function(t){for(var e=this.w,i=0;i<t.length;i++)e.globals.collapsedSeriesIndices.indexOf(i)>-1&&(t[i].data=[]);return t}},{key:"toggleSeriesOnHover",value:function(t,e){var i=this.w,a=i.globals.dom.baseEl.querySelectorAll(".apexcharts-series, .apexcharts-datalabels");if("mousemove"===t.type){var s=parseInt(e.getAttribute("rel"),10)-1,r=null,n=null;i.globals.axisCharts||"radialBar"===i.config.chart.type?i.globals.axisCharts?(r=i.globals.dom.baseEl.querySelector(".apexcharts-series[data\\:realIndex='".concat(s,"']")),n=i.globals.dom.baseEl.querySelector(".apexcharts-datalabels[data\\:realIndex='".concat(s,"']"))):r=i.globals.dom.baseEl.querySelector(".apexcharts-series[rel='".concat(s+1,"']")):r=i.globals.dom.baseEl.querySelector(".apexcharts-series[rel='".concat(s+1,"'] path"));for(var o=0;o<a.length;o++)a[o].classList.add(this.legendInactiveClass);null!==r&&(i.globals.axisCharts||r.parentNode.classList.remove(this.legendInactiveClass),r.classList.remove(this.legendInactiveClass),null!==n&&n.classList.remove(this.legendInactiveClass))}else if("mouseout"===t.type)for(var l=0;l<a.length;l++)a[l].classList.remove(this.legendInactiveClass)}},{key:"highlightRangeInSeries",value:function(t,e){var i=this,a=this.w,s=a.globals.dom.baseEl.getElementsByClassName("apexcharts-heatmap-rect"),r=function(t){for(var e=0;e<s.length;e++)s[e].classList[t](i.legendInactiveClass)};if("mousemove"===t.type){var n=parseInt(e.getAttribute("rel"),10)-1;r("add"),function(t){for(var e=0;e<s.length;e++){var a=parseInt(s[e].getAttribute("val"),10);a>=t.from&&a<=t.to&&s[e].classList.remove(i.legendInactiveClass)}}(a.config.plotOptions.heatmap.colorScale.ranges[n])}else"mouseout"===t.type&&r("remove")}},{key:"getActiveConfigSeriesIndex",value:function(){var t=arguments.length>0&&void 0!==arguments[0]&&arguments[0],e=this.w,i=0;if(e.config.series.length>1)for(var a=e.config.series.map((function(i,a){var s=!1;return t&&(s="bar"===e.config.series[a].type||"column"===e.config.series[a].type),i.data&&i.data.length>0&&!s?a:-1})),s=0;s<a.length;s++)if(-1!==a[s]){i=a[s];break}return i}},{key:"getPreviousPaths",value:function(){var t=this.w;function e(e,i,a){for(var s=e[i].childNodes,r={type:a,paths:[],realIndex:e[i].getAttribute("data:realIndex")},n=0;n<s.length;n++)if(s[n].hasAttribute("pathTo")){var o=s[n].getAttribute("pathTo");r.paths.push({d:o})}t.globals.previousPaths.push(r)}t.globals.previousPaths=[];["line","area","bar","candlestick","radar"].forEach((function(i){for(var a,s=(a=i,t.globals.dom.baseEl.querySelectorAll(".apexcharts-".concat(a,"-series .apexcharts-series"))),r=0;r<s.length;r++)e(s,r,i)})),this.handlePrevBubbleScatterPaths("bubble"),this.handlePrevBubbleScatterPaths("scatter");var i=t.globals.dom.baseEl.querySelectorAll(".apexcharts-heatmap .apexcharts-series");if(i.length>0)for(var a=0;a<i.length;a++){for(var s=t.globals.dom.baseEl.querySelectorAll(".apexcharts-heatmap .apexcharts-series[data\\:realIndex='".concat(a,"'] rect")),r=[],n=0;n<s.length;n++)r.push({color:s[n].getAttribute("color")});t.globals.previousPaths.push(r)}t.globals.axisCharts||(t.globals.previousPaths=t.globals.series)}},{key:"handlePrevBubbleScatterPaths",value:function(t){var e=this.w,i=e.globals.dom.baseEl.querySelectorAll(".apexcharts-".concat(t,"-series .apexcharts-series"));if(i.length>0)for(var a=0;a<i.length;a++){for(var s=e.globals.dom.baseEl.querySelectorAll(".apexcharts-".concat(t,"-series .apexcharts-series[data\\:realIndex='").concat(a,"'] circle")),r=[],n=0;n<s.length;n++)r.push({x:s[n].getAttribute("cx"),y:s[n].getAttribute("cy"),r:s[n].getAttribute("r")});e.globals.previousPaths.push(r)}}},{key:"clearPreviousPaths",value:function(){var t=this.w;t.globals.previousPaths=[],t.globals.allSeriesCollapsed=!1}},{key:"handleNoData",value:function(){var t=this.w,e=t.config.noData,i=new b(this.ctx),a=t.globals.svgWidth/2,s=t.globals.svgHeight/2,r="middle";if(t.globals.noData=!0,t.globals.animationEnded=!0,"left"===e.align?(a=10,r="start"):"right"===e.align&&(a=t.globals.svgWidth-10,r="end"),"top"===e.verticalAlign?s=50:"bottom"===e.verticalAlign&&(s=t.globals.svgHeight-50),a+=e.offsetX,s=s+parseInt(e.style.fontSize,10)+2+e.offsetY,void 0!==e.text&&""!==e.text){var n=i.drawText({x:a,y:s,text:e.text,textAnchor:r,fontSize:e.style.fontSize,fontFamily:e.style.fontFamily,foreColor:e.style.color,opacity:1,class:"apexcharts-text-nodata"});t.globals.dom.Paper.add(n)}}},{key:"setNullSeriesToZeroValues",value:function(t){for(var e=this.w,i=0;i<t.length;i++)if(0===t[i].length)for(var a=0;a<t[e.globals.maxValsInArrayIndex].length;a++)t[i].push(0);return t}},{key:"hasAllSeriesEqualX",value:function(){for(var t=!0,e=this.w,i=this.filteredSeriesX(),a=0;a<i.length-1;a++)if(i[a][0]!==i[a+1][0]){t=!1;break}return e.globals.allSeriesHasEqualX=t,t}},{key:"filteredSeriesX",value:function(){var t=this.w.globals.seriesX.map((function(t){return t.length>0?t:[]}));return t}}]),t}(),Y=function(){function t(i){e(this,t),this.w=i.w,this.barCtx=i}return a(t,[{key:"initVariables",value:function(t){var e=this.w;this.barCtx.series=t,this.barCtx.totalItems=0,this.barCtx.seriesLen=0,this.barCtx.visibleI=-1,this.barCtx.visibleItems=1;for(var i=0;i<t.length;i++)if(t[i].length>0&&(this.barCtx.seriesLen=this.barCtx.seriesLen+1,this.barCtx.totalItems+=t[i].length),e.globals.isXNumeric)for(var a=0;a<t[i].length;a++)e.globals.seriesX[i][a]>e.globals.minX&&e.globals.seriesX[i][a]<e.globals.maxX&&this.barCtx.visibleItems++;else this.barCtx.visibleItems=e.globals.dataPoints;0===this.barCtx.seriesLen&&(this.barCtx.seriesLen=1)}},{key:"initialPositions",value:function(){var t,e,i,a,s,r,n,o,l=this.w,h=l.globals.dataPoints;if(this.barCtx.isTimelineBar&&(h=l.globals.labels.length),this.barCtx.isHorizontal)s=(i=l.globals.gridHeight/h)/this.barCtx.seriesLen,l.globals.isXNumeric&&(s=(i=l.globals.gridHeight/this.barCtx.totalItems)/this.barCtx.seriesLen),s=s*parseInt(this.barCtx.barOptions.barHeight,10)/100,o=this.barCtx.baseLineInvertedY+l.globals.padHorizontal+(this.barCtx.isReversed?l.globals.gridWidth:0)-(this.barCtx.isReversed?2*this.barCtx.baseLineInvertedY:0),e=(i-s*this.barCtx.seriesLen)/2;else{if(a=l.globals.gridWidth/this.barCtx.visibleItems,l.config.xaxis.convertedCatToNumeric&&(a=l.globals.gridWidth/l.globals.dataPoints),r=a/this.barCtx.seriesLen*parseInt(this.barCtx.barOptions.columnWidth,10)/100,l.globals.isXNumeric){var c=this.barCtx.xRatio;l.config.xaxis.convertedCatToNumeric&&(c=this.barCtx.initialXRatio),l.globals.minXDiff&&.5!==l.globals.minXDiff&&l.globals.minXDiff/c>0&&(a=l.globals.minXDiff/c),(r=a/this.barCtx.seriesLen*parseInt(this.barCtx.barOptions.columnWidth,10)/100)<1&&(r=1)}n=l.globals.gridHeight-this.barCtx.baseLineY[this.barCtx.yaxisIndex]-(this.barCtx.isReversed?l.globals.gridHeight:0)+(this.barCtx.isReversed?2*this.barCtx.baseLineY[this.barCtx.yaxisIndex]:0),t=l.globals.padHorizontal+(a-r*this.barCtx.seriesLen)/2}return{x:t,y:e,yDivision:i,xDivision:a,barHeight:s,barWidth:r,zeroH:n,zeroW:o}}},{key:"getPathFillColor",value:function(t,e,i,a){var s=this.w,r=new T(this.barCtx.ctx),n=null,o=this.barCtx.barOptions.distributed?i:e;this.barCtx.barOptions.colors.ranges.length>0&&this.barCtx.barOptions.colors.ranges.map((function(a){t[e][i]>=a.from&&t[e][i]<=a.to&&(n=a.color)}));return s.config.series[e].data[i]&&s.config.series[e].data[i].fillColor&&(n=s.config.series[e].data[i].fillColor),r.fillPath({seriesNumber:this.barCtx.barOptions.distributed?o:a,dataPointIndex:i,color:n,value:t[e][i]})}},{key:"getStrokeWidth",value:function(t,e,i){var a=0,s=this.w;return void 0===this.barCtx.series[t][e]||null===this.barCtx.series[t][e]?this.barCtx.isNullValue=!0:this.barCtx.isNullValue=!1,s.config.stroke.show&&(this.barCtx.isNullValue||(a=Array.isArray(this.barCtx.strokeWidth)?this.barCtx.strokeWidth[i]:this.barCtx.strokeWidth)),a}},{key:"barBackground",value:function(t){var e=t.j,i=t.i,a=t.x1,s=t.x2,r=t.y1,n=t.y2,o=t.elSeries,l=this.w,h=new b(this.barCtx.ctx),c=new X(this.barCtx.ctx).getActiveConfigSeriesIndex();if(this.barCtx.barOptions.colors.backgroundBarColors.length>0&&c===i){e>=this.barCtx.barOptions.colors.backgroundBarColors.length&&(e-=this.barCtx.barOptions.colors.backgroundBarColors.length);var d=this.barCtx.barOptions.colors.backgroundBarColors[e],g=h.drawRect(void 0!==a?a:0,void 0!==r?r:0,void 0!==s?s:l.globals.gridWidth,void 0!==n?n:l.globals.gridHeight,this.barCtx.barOptions.colors.backgroundBarRadius,d,this.barCtx.barOptions.colors.backgroundBarOpacity);o.add(g),g.node.classList.add("apexcharts-backgroundBar")}}},{key:"getColumnPaths",value:function(t){var e=t.barWidth,i=t.barXPosition,a=t.yRatio,s=t.y1,r=t.y2,n=t.strokeWidth,o=t.series,l=t.realIndex,h=t.i,c=t.j,d=t.w,g=new b(this.barCtx.ctx);(n=Array.isArray(n)?n[l]:n)||(n=0);var u={barWidth:e,strokeWidth:n,yRatio:a,barXPosition:i,y1:s,y2:r},f=this.getRoundedBars(d,u,o,h,c),p=i,x=i+e,m=g.move(p,f.y1),v=g.move(p,f.y1);return d.globals.previousPaths.length>0&&(v=this.barCtx.getPreviousPath(l,c,!1)),{pathTo:m=m+g.line(p,f.y2)+f.endingPath+g.line(x-n,f.y2)+g.line(x-n,f.y1)+f.startingPath+"z",pathFrom:v=v+g.line(p,s)+g.line(x-n,s)+g.line(x-n,s)+g.line(x-n,s)+g.line(p,s)}}},{key:"getBarpaths",value:function(t){var e=t.barYPosition,i=t.barHeight,a=t.x1,s=t.x2,r=t.strokeWidth,n=t.series,o=t.realIndex,l=t.i,h=t.j,c=t.w,d=new b(this.barCtx.ctx);(r=Array.isArray(r)?r[o]:r)||(r=0);var g={barHeight:i,strokeWidth:r,barYPosition:e,x2:s,x1:a},u=this.getRoundedBars(c,g,n,l,h),f=d.move(u.x1,e),p=d.move(u.x1,e);c.globals.previousPaths.length>0&&(p=this.barCtx.getPreviousPath(o,h,!1));var x=e,m=e+i;return{pathTo:f=f+d.line(u.x2,x)+u.endingPath+d.line(u.x2,m-r)+d.line(u.x1,m-r)+u.startingPath+"z",pathFrom:p=p+d.line(a,x)+d.line(a,m-r)+d.line(a,m-r)+d.line(a,m-r)+d.line(a,x)}}},{key:"getRoundedBars",value:function(t,e,i,a,s){var r=new b(this.barCtx.ctx),n=Array.isArray(e.strokeWidth)?e.strokeWidth[a]:e.strokeWidth;if(n||(n=0),this.barCtx.isHorizontal){var o=null,l="",h=e.x2,c=e.x1;if(void 0!==i[a][s]||null!==i[a][s]){var d=i[a][s]<0,g=e.barHeight/2-n;switch(d&&(g=-e.barHeight/2-n),g>Math.abs(h-c)&&(g=Math.abs(h-c)),"rounded"===this.barCtx.barOptions.endingShape&&(h=e.x2-g/2),"rounded"===this.barCtx.barOptions.startingShape&&(c=e.x1+g/2),this.barCtx.barOptions.endingShape){case"flat":o=r.line(h,e.barYPosition+e.barHeight-n);break;case"rounded":o=r.quadraticCurve(h+g,e.barYPosition+(e.barHeight-n)/2,h,e.barYPosition+e.barHeight-n)}switch(this.barCtx.barOptions.startingShape){case"flat":l=r.line(c,e.barYPosition+e.barHeight-n);break;case"rounded":l=r.quadraticCurve(c-g,e.barYPosition+e.barHeight/2,c,e.barYPosition)}}return{endingPath:o,startingPath:l,x2:h,x1:c}}var u=null,f="",p=e.y2,x=e.y1;if(void 0!==i[a][s]||null!==i[a][s]){var m=i[a][s]<0,v=e.barWidth/2-n;switch(m&&(v=-e.barWidth/2-n),v>Math.abs(p-x)&&(v=Math.abs(p-x)),"rounded"===this.barCtx.barOptions.endingShape&&(p+=v/2),"rounded"===this.barCtx.barOptions.startingShape&&(x-=v/2),this.barCtx.barOptions.endingShape){case"flat":u=r.line(e.barXPosition+e.barWidth-n,p);break;case"rounded":u=r.quadraticCurve(e.barXPosition+(e.barWidth-n)/2,p-v,e.barXPosition+e.barWidth-n,p)}switch(this.barCtx.barOptions.startingShape){case"flat":f=r.line(e.barXPosition+e.barWidth-n,x);break;case"rounded":f=r.quadraticCurve(e.barXPosition+(e.barWidth-n)/2,x+v,e.barXPosition,x)}}return{endingPath:u,startingPath:f,y2:p,y1:x}}}]),t}(),F=function(){function t(i,a){e(this,t),this.ctx=i,this.w=i.w;var s=this.w;this.barOptions=s.config.plotOptions.bar,this.isHorizontal=this.barOptions.horizontal,this.strokeWidth=s.config.stroke.width,this.isNullValue=!1,this.isTimelineBar="datetime"===s.config.xaxis.type&&s.globals.seriesRangeBarTimeline.length,this.xyRatios=a,null!==this.xyRatios&&(this.xRatio=a.xRatio,this.initialXRatio=a.initialXRatio,this.yRatio=a.yRatio,this.invertedXRatio=a.invertedXRatio,this.invertedYRatio=a.invertedYRatio,this.baseLineY=a.baseLineY,this.baseLineInvertedY=a.baseLineInvertedY),this.yaxisIndex=0,this.seriesLen=0,this.barHelpers=new Y(this)}return a(t,[{key:"draw",value:function(t,e){var i=this.w,a=new b(this.ctx),s=new y(this.ctx,i);t=s.getLogSeries(t),this.series=t,this.yRatio=s.getLogYRatios(this.yRatio),this.barHelpers.initVariables(t);var r=a.group({class:"apexcharts-bar-series apexcharts-plot-series"});i.config.dataLabels.enabled&&this.totalItems>this.barOptions.dataLabels.maxItems&&console.warn("WARNING: DataLabels are enabled but there are too many to display. This may cause performance issue when rendering.");for(var o=0,l=0;o<t.length;o++,l++){var h,c,d,g,u=void 0,p=void 0,x=[],m=[],v=i.globals.comboCharts?e[o]:o,w=a.group({class:"apexcharts-series",rel:o+1,seriesName:f.escapeString(i.globals.seriesNames[v]),"data:realIndex":v});this.ctx.series.addCollapsedClassToSeries(w,v),t[o].length>0&&(this.visibleI=this.visibleI+1);var k=0,A=0;this.yRatio.length>1&&(this.yaxisIndex=v),this.isReversed=i.config.yaxis[this.yaxisIndex]&&i.config.yaxis[this.yaxisIndex].reversed;var S=this.barHelpers.initialPositions();p=S.y,k=S.barHeight,c=S.yDivision,g=S.zeroW,u=S.x,A=S.barWidth,h=S.xDivision,d=S.zeroH,this.horizontal||m.push(u+A/2);for(var C=a.group({class:"apexcharts-datalabels","data:realIndex":v}),L=0;L<i.globals.dataPoints;L++){var P=this.barHelpers.getStrokeWidth(o,L,v),T=null,z={indexes:{i:o,j:L,realIndex:v,bc:l},x:u,y:p,strokeWidth:P,elSeries:w};this.isHorizontal?(T=this.drawBarPaths(n(n({},z),{},{barHeight:k,zeroW:g,yDivision:c})),A=this.series[o][L]/this.invertedYRatio):(T=this.drawColumnPaths(n(n({},z),{},{xDivision:h,barWidth:A,zeroH:d})),k=this.series[o][L]/this.yRatio[this.yaxisIndex]),p=T.y,u=T.x,L>0&&m.push(u+A/2),x.push(p);var I=this.barHelpers.getPathFillColor(t,o,L,v);this.renderSeries({realIndex:v,pathFill:I,j:L,i:o,pathFrom:T.pathFrom,pathTo:T.pathTo,strokeWidth:P,elSeries:w,x:u,y:p,series:t,barHeight:k,barWidth:A,elDataLabelsWrap:C,visibleSeries:this.visibleI,type:"bar"})}i.globals.seriesXvalues[v]=m,i.globals.seriesYvalues[v]=x,r.add(w)}return r}},{key:"renderSeries",value:function(t){var e=t.realIndex,i=t.pathFill,a=t.lineFill,s=t.j,r=t.i,n=t.pathFrom,o=t.pathTo,l=t.strokeWidth,h=t.elSeries,c=t.x,d=t.y,g=t.y1,u=t.y2,f=t.series,x=t.barHeight,m=t.barWidth,v=t.barYPosition,y=t.elDataLabelsWrap,w=t.visibleSeries,k=t.type,A=this.w,S=new b(this.ctx);a||(a=this.barOptions.distributed?A.globals.stroke.colors[s]:A.globals.stroke.colors[e]),A.config.series[r].data[s]&&A.config.series[r].data[s].strokeColor&&(a=A.config.series[r].data[s].strokeColor),this.isNullValue&&(i="none");var C=s/A.config.chart.animations.animateGradually.delay*(A.config.chart.animations.speed/A.globals.dataPoints)/2.4,L=S.renderPaths({i:r,j:s,realIndex:e,pathFrom:n,pathTo:o,stroke:a,strokeWidth:l,strokeLineCap:A.config.stroke.lineCap,fill:i,animationDelay:C,initialSpeed:A.config.chart.animations.speed,dataChangeSpeed:A.config.chart.animations.dynamicAnimation.speed,className:"apexcharts-".concat(k,"-area")});L.attr("clip-path","url(#gridRectMask".concat(A.globals.cuid,")")),void 0!==g&&void 0!==u&&(L.attr("data-range-y1",g),L.attr("data-range-y2",u)),new p(this.ctx).setSelectionFilter(L,e,s),h.add(L);var P=new E(this).handleBarDataLabels({x:c,y:d,y1:g,y2:u,i:r,j:s,series:f,realIndex:e,barHeight:x,barWidth:m,barYPosition:v,renderedPath:L,visibleSeries:w});return null!==P&&y.add(P),h.add(y),h}},{key:"drawBarPaths",value:function(t){var e=t.indexes,i=t.barHeight,a=t.strokeWidth,s=t.zeroW,r=t.x,n=t.y,o=t.yDivision,l=t.elSeries,h=this.w,c=e.i,d=e.j;h.globals.isXNumeric&&(n=(h.globals.seriesX[c][d]-h.globals.minX)/this.invertedXRatio-i);var g=n+i*this.visibleI;r=void 0===this.series[c][d]||null===this.series[c][d]?s:s+this.series[c][d]/this.invertedYRatio-2*(this.isReversed?this.series[c][d]/this.invertedYRatio:0);var u=this.barHelpers.getBarpaths({barYPosition:g,barHeight:i,x1:s,x2:r,strokeWidth:a,series:this.series,realIndex:e.realIndex,i:c,j:d,w:h});return h.globals.isXNumeric||(n+=o),this.barHelpers.barBackground({j:d,i:c,y1:g-i*this.visibleI,y2:i*this.seriesLen,elSeries:l}),{pathTo:u.pathTo,pathFrom:u.pathFrom,x:r,y:n,barYPosition:g}}},{key:"drawColumnPaths",value:function(t){var e=t.indexes,i=t.x,a=t.y,s=t.xDivision,r=t.barWidth,n=t.zeroH,o=t.strokeWidth,l=t.elSeries,h=this.w,c=e.i,d=e.j,g=e.bc;if(h.globals.isXNumeric){var u=c;h.globals.seriesX[c].length||(u=h.globals.maxValsInArrayIndex),i=(h.globals.seriesX[u][d]-h.globals.minX)/this.xRatio-r*this.seriesLen/2}var f=i+r*this.visibleI;a=void 0===this.series[c][d]||null===this.series[c][d]?n:n-this.series[c][d]/this.yRatio[this.yaxisIndex]+2*(this.isReversed?this.series[c][d]/this.yRatio[this.yaxisIndex]:0);var p=this.barHelpers.getColumnPaths({barXPosition:f,barWidth:r,y1:n,y2:a,strokeWidth:o,series:this.series,realIndex:e.realIndex,i:c,j:d,w:h});return h.globals.isXNumeric||(i+=s),this.barHelpers.barBackground({bc:g,j:d,i:c,x1:f-o/2-r*this.visibleI,x2:r*this.seriesLen+o/2,elSeries:l}),{pathTo:p.pathTo,pathFrom:p.pathFrom,x:i,y:a,barXPosition:f}}},{key:"getPreviousPath",value:function(t,e){for(var i,a=this.w,s=0;s<a.globals.previousPaths.length;s++){var r=a.globals.previousPaths[s];r.paths&&r.paths.length>0&&parseInt(r.realIndex,10)===parseInt(t,10)&&void 0!==a.globals.previousPaths[s].paths[e]&&(i=a.globals.previousPaths[s].paths[e].d)}return i}}]),t}(),R=function(){function t(i){e(this,t),this.ctx=i,this.w=i.w,this.months31=[1,3,5,7,8,10,12],this.months30=[2,4,6,9,11],this.daysCntOfYear=[0,31,59,90,120,151,181,212,243,273,304,334]}return a(t,[{key:"isValidDate",value:function(t){return!isNaN(this.parseDate(t))}},{key:"getTimeStamp",value:function(t){return Date.parse(t)?this.w.config.xaxis.labels.datetimeUTC?new Date(new Date(t).toISOString().substr(0,25)).getTime():new Date(t).getTime():t}},{key:"getDate",value:function(t){return this.w.config.xaxis.labels.datetimeUTC?new Date(new Date(t).toUTCString()):new Date(t)}},{key:"parseDate",value:function(t){var e=Date.parse(t);if(!isNaN(e))return this.getTimeStamp(t);var i=Date.parse(t.replace(/-/g,"/").replace(/[a-z]+/gi," "));return i=this.getTimeStamp(i)}},{key:"formatDate",value:function(t,e){var i=this.w.globals.locale,a=this.w.config.xaxis.labels.datetimeUTC,s=["\0"].concat(g(i.months)),r=["\x01"].concat(g(i.shortMonths)),n=["\x02"].concat(g(i.days)),o=["\x03"].concat(g(i.shortDays));function l(t,e){var i=t+"";for(e=e||2;i.length<e;)i="0"+i;return i}var h=a?t.getUTCFullYear():t.getFullYear();e=(e=(e=e.replace(/(^|[^\\])yyyy+/g,"$1"+h)).replace(/(^|[^\\])yy/g,"$1"+h.toString().substr(2,2))).replace(/(^|[^\\])y/g,"$1"+h);var c=(a?t.getUTCMonth():t.getMonth())+1;e=(e=(e=(e=e.replace(/(^|[^\\])MMMM+/g,"$1"+s[0])).replace(/(^|[^\\])MMM/g,"$1"+r[0])).replace(/(^|[^\\])MM/g,"$1"+l(c))).replace(/(^|[^\\])M/g,"$1"+c);var d=a?t.getUTCDate():t.getDate();e=(e=(e=(e=e.replace(/(^|[^\\])dddd+/g,"$1"+n[0])).replace(/(^|[^\\])ddd/g,"$1"+o[0])).replace(/(^|[^\\])dd/g,"$1"+l(d))).replace(/(^|[^\\])d/g,"$1"+d);var u=a?t.getUTCHours():t.getHours(),f=u>12?u-12:0===u?12:u;e=(e=(e=(e=e.replace(/(^|[^\\])HH+/g,"$1"+l(u))).replace(/(^|[^\\])H/g,"$1"+u)).replace(/(^|[^\\])hh+/g,"$1"+l(f))).replace(/(^|[^\\])h/g,"$1"+f);var p=a?t.getUTCMinutes():t.getMinutes();e=(e=e.replace(/(^|[^\\])mm+/g,"$1"+l(p))).replace(/(^|[^\\])m/g,"$1"+p);var x=a?t.getUTCSeconds():t.getSeconds();e=(e=e.replace(/(^|[^\\])ss+/g,"$1"+l(x))).replace(/(^|[^\\])s/g,"$1"+x);var b=a?t.getUTCMilliseconds():t.getMilliseconds();e=e.replace(/(^|[^\\])fff+/g,"$1"+l(b,3)),b=Math.round(b/10),e=e.replace(/(^|[^\\])ff/g,"$1"+l(b)),b=Math.round(b/10);var m=u<12?"AM":"PM";e=(e=(e=e.replace(/(^|[^\\])f/g,"$1"+b)).replace(/(^|[^\\])TT+/g,"$1"+m)).replace(/(^|[^\\])T/g,"$1"+m.charAt(0));var v=m.toLowerCase();e=(e=e.replace(/(^|[^\\])tt+/g,"$1"+v)).replace(/(^|[^\\])t/g,"$1"+v.charAt(0));var y=-t.getTimezoneOffset(),w=a||!y?"Z":y>0?"+":"-";if(!a){var k=(y=Math.abs(y))%60;w+=l(Math.floor(y/60))+":"+l(k)}e=e.replace(/(^|[^\\])K/g,"$1"+w);var A=(a?t.getUTCDay():t.getDay())+1;return e=(e=(e=(e=(e=e.replace(new RegExp(n[0],"g"),n[A])).replace(new RegExp(o[0],"g"),o[A])).replace(new RegExp(s[0],"g"),s[c])).replace(new RegExp(r[0],"g"),r[c])).replace(/\\(.)/g,"$1")}},{key:"getTimeUnitsfromTimestamp",value:function(t,e,i){var a=this.w;void 0!==a.config.xaxis.min&&(t=a.config.xaxis.min),void 0!==a.config.xaxis.max&&(e=a.config.xaxis.max);var s=this.getDate(t),r=this.getDate(e),n=this.formatDate(s,"yyyy MM dd HH mm").split(" "),o=this.formatDate(r,"yyyy MM dd HH mm").split(" ");return{minMinute:parseInt(n[4],10),maxMinute:parseInt(o[4],10),minHour:parseInt(n[3],10),maxHour:parseInt(o[3],10),minDate:parseInt(n[2],10),maxDate:parseInt(o[2],10),minMonth:parseInt(n[1],10)-1,maxMonth:parseInt(o[1],10)-1,minYear:parseInt(n[0],10),maxYear:parseInt(o[0],10)}}},{key:"isLeapYear",value:function(t){return t%4==0&&t%100!=0||t%400==0}},{key:"calculcateLastDaysOfMonth",value:function(t,e,i){return this.determineDaysOfMonths(t,e)-i}},{key:"determineDaysOfYear",value:function(t){var e=365;return this.isLeapYear(t)&&(e=366),e}},{key:"determineRemainingDaysOfYear",value:function(t,e,i){var a=this.daysCntOfYear[e]+i;return e>1&&this.isLeapYear()&&a++,a}},{key:"determineDaysOfMonths",value:function(t,e){var i=30;switch(t=f.monthMod(t),!0){case this.months30.indexOf(t)>-1:2===t&&(i=this.isLeapYear(e)?29:28);break;case this.months31.indexOf(t)>-1:default:i=31}return i}}]),t}(),D=function(t){o(s,t);var i=d(s);function s(){return e(this,s),i.apply(this,arguments)}return a(s,[{key:"draw",value:function(t,e){var i=this.w,a=new b(this.ctx);this.rangeBarOptions=this.w.config.plotOptions.rangeBar,this.series=t,this.seriesRangeStart=i.globals.seriesRangeStart,this.seriesRangeEnd=i.globals.seriesRangeEnd,this.barHelpers.initVariables(t);for(var s=a.group({class:"apexcharts-rangebar-series apexcharts-plot-series"}),r=0;r<t.length;r++){var o,l,h,c=void 0,d=void 0,g=void 0,u=i.globals.comboCharts?e[r]:r,p=a.group({class:"apexcharts-series",seriesName:f.escapeString(i.globals.seriesNames[u]),rel:r+1,"data:realIndex":u});t[r].length>0&&(this.visibleI=this.visibleI+1);var x=0,m=0;this.yRatio.length>1&&(this.yaxisIndex=u);var v=this.barHelpers.initialPositions();d=v.y,h=v.zeroW,c=v.x,m=v.barWidth,o=v.xDivision,l=v.zeroH;for(var y=a.group({class:"apexcharts-datalabels","data:realIndex":u}),w=0;w<i.globals.dataPoints;w++){var k=this.barHelpers.getStrokeWidth(r,w,u),A=this.seriesRangeStart[r][w],S=this.seriesRangeEnd[r][w],C=null,L=null,P={x:c,y:d,strokeWidth:k,elSeries:p};if(g=v.yDivision,x=v.barHeight,this.isHorizontal){L=d+x*this.visibleI;var T=(g-x*this.seriesLen)/2;if(void 0===i.config.series[r].data[w])break;if(this.isTimelineBar&&i.config.series[r].data[w].x){var z=this.detectOverlappingBars({i:r,j:w,barYPosition:L,srty:T,barHeight:x,yDivision:g,initPositions:v});x=z.barHeight,L=z.barYPosition}m=(C=this.drawRangeBarPaths(n({indexes:{i:r,j:w,realIndex:u},barHeight:x,barYPosition:L,zeroW:h,yDivision:g,y1:A,y2:S},P))).barWidth}else x=(C=this.drawRangeColumnPaths(n({indexes:{i:r,j:w,realIndex:u},zeroH:l,barWidth:m,xDivision:o},P))).barHeight;d=C.y,c=C.x;var I=this.barHelpers.getPathFillColor(t,r,w,u),M=i.globals.stroke.colors[u];this.renderSeries({realIndex:u,pathFill:I,lineFill:M,j:w,i:r,x:c,y:d,y1:A,y2:S,pathFrom:C.pathFrom,pathTo:C.pathTo,strokeWidth:k,elSeries:p,series:t,barHeight:x,barYPosition:L,barWidth:m,elDataLabelsWrap:y,visibleSeries:this.visibleI,type:"rangebar"})}s.add(p)}return s}},{key:"detectOverlappingBars",value:function(t){var e=t.i,i=t.j,a=t.barYPosition,s=t.srty,r=t.barHeight,n=t.yDivision,o=t.initPositions,l=this.w,h=[],c=l.config.series[e].data[i].rangeName,d=l.config.series[e].data[i].x,g=l.globals.labels.indexOf(d),u=l.globals.seriesRangeBarTimeline[e].findIndex((function(t){return t.x===d&&t.overlaps.length>0}));return a=s+r*this.visibleI+n*g,u>-1&&!l.config.plotOptions.bar.rangeBarOverlap&&(h=l.globals.seriesRangeBarTimeline[e][u].overlaps).indexOf(c)>-1&&(a=(r=o.barHeight/h.length)*this.visibleI+n*(100-parseInt(this.barOptions.barHeight,10))/100/2+r*(this.visibleI+h.indexOf(c))+n*g),{barYPosition:a,barHeight:r}}},{key:"drawRangeColumnPaths",value:function(t){var e=t.indexes,i=t.x,a=(t.strokeWidth,t.xDivision),s=t.barWidth,r=t.zeroH,n=this.w,o=e.i,l=e.j,h=this.yRatio[this.yaxisIndex],c=e.realIndex,d=this.getRangeValue(c,l),g=Math.min(d.start,d.end),u=Math.max(d.start,d.end);n.globals.isXNumeric&&(i=(n.globals.seriesX[o][l]-n.globals.minX)/this.xRatio-s/2);var f=i+s*this.visibleI;void 0===this.series[o][l]||null===this.series[o][l]?g=r:(g=r-g/h,u=r-u/h);var p=Math.abs(u-g),x=this.barHelpers.getColumnPaths({barXPosition:f,barWidth:s,y1:g,y2:u,strokeWidth:this.strokeWidth,series:this.seriesRangeEnd,i:c,j:l,w:n});return n.globals.isXNumeric||(i+=a),{pathTo:x.pathTo,pathFrom:x.pathFrom,barHeight:p,x:i,y:u,barXPosition:f}}},{key:"drawRangeBarPaths",value:function(t){var e=t.indexes,i=t.y,a=t.y1,s=t.y2,r=t.yDivision,n=t.barHeight,o=t.barYPosition,l=t.zeroW,h=this.w,c=l+a/this.invertedYRatio,d=l+s/this.invertedYRatio,g=Math.abs(d-c),u=this.barHelpers.getBarpaths({barYPosition:o,barHeight:n,x1:c,x2:d,strokeWidth:this.strokeWidth,series:this.seriesRangeEnd,i:e.realIndex,j:e.j,w:h});return h.globals.isXNumeric||(i+=r),{pathTo:u.pathTo,pathFrom:u.pathFrom,barWidth:g,x:d,y:i}}},{key:"getRangeValue",value:function(t,e){var i=this.w;return{start:i.globals.seriesRangeStart[t][e],end:i.globals.seriesRangeEnd[t][e]}}},{key:"getTooltipValues",value:function(t){var e=t.ctx,i=t.seriesIndex,a=t.dataPointIndex,s=t.y1,r=t.y2,n=t.w,o=n.globals.seriesRangeStart[i][a],l=n.globals.seriesRangeEnd[i][a],h=n.globals.labels[a],c=n.config.series[i].name,d=n.config.tooltip.y.formatter,g=n.config.tooltip.y.title.formatter,u={w:n,seriesIndex:i,dataPointIndex:a};"function"==typeof g&&(c=g(c,u)),s&&r&&(o=s,l=r,n.config.series[i].data[a].x&&(h=n.config.series[i].data[a].x+":"),"function"==typeof d&&(h=d(h,u)));var f="",p="",x=n.globals.colors[i];if(void 0===n.config.tooltip.x.formatter)if("datetime"===n.config.xaxis.type){var b=new R(e);f=b.formatDate(b.getDate(o),n.config.tooltip.x.format),p=b.formatDate(b.getDate(l),n.config.tooltip.x.format)}else f=o,p=l;else f=n.config.tooltip.x.formatter(o),p=n.config.tooltip.x.formatter(l);return{start:o,end:l,startVal:f,endVal:p,ylabel:h,color:x,seriesName:c}}},{key:"buildCustomTooltipHTML",value:function(t){var e=t.color,i=t.seriesName;return'<div class="apexcharts-tooltip-rangebar"><div> <span class="series-name" style="color: '+e+'">'+(i||"")+'</span></div><div> <span class="category">'+t.ylabel+' </span> <span class="value start-value">'+t.start+'</span> <span class="separator">-</span> <span class="value end-value">'+t.end+"</span></div></div>"}}]),s}(F),H=function(){function t(i){e(this,t),this.opts=i}return a(t,[{key:"line",value:function(){return{chart:{animations:{easing:"swing"}},dataLabels:{enabled:!1},stroke:{width:5,curve:"straight"},markers:{size:0,hover:{sizeOffset:6}},xaxis:{crosshairs:{width:1}}}}},{key:"sparkline",value:function(t){this.opts.yaxis[0].show=!1,this.opts.yaxis[0].title.text="",this.opts.yaxis[0].axisBorder.show=!1,this.opts.yaxis[0].axisTicks.show=!1,this.opts.yaxis[0].floating=!0;return f.extend(t,{grid:{show:!1,padding:{left:0,right:0,top:0,bottom:0}},legend:{show:!1},xaxis:{labels:{show:!1},tooltip:{enabled:!1},axisBorder:{show:!1},axisTicks:{show:!1}},chart:{toolbar:{show:!1},zoom:{enabled:!1}},dataLabels:{enabled:!1}})}},{key:"bar",value:function(){return{chart:{stacked:!1,animations:{easing:"swing"}},plotOptions:{bar:{dataLabels:{position:"center"}}},dataLabels:{style:{colors:["#fff"]},background:{enabled:!1}},stroke:{width:0,lineCap:"square"},fill:{opacity:.85},legend:{markers:{shape:"square",radius:2,size:8}},tooltip:{shared:!1},xaxis:{tooltip:{enabled:!1},tickPlacement:"between",crosshairs:{width:"barWidth",position:"back",fill:{type:"gradient"},dropShadow:{enabled:!1},stroke:{width:0}}}}}},{key:"candlestick",value:function(){return{stroke:{width:1,colors:["#333"]},fill:{opacity:1},dataLabels:{enabled:!1},tooltip:{shared:!0,custom:function(t){var e=t.seriesIndex,i=t.dataPointIndex,a=t.w;return'<div class="apexcharts-tooltip-candlestick"><div>Open: <span class="value">'+a.globals.seriesCandleO[e][i]+'</span></div><div>High: <span class="value">'+a.globals.seriesCandleH[e][i]+'</span></div><div>Low: <span class="value">'+a.globals.seriesCandleL[e][i]+'</span></div><div>Close: <span class="value">'+a.globals.seriesCandleC[e][i]+"</span></div></div>"}},states:{active:{filter:{type:"none"}}},xaxis:{crosshairs:{width:1}}}}},{key:"rangeBar",value:function(){return{stroke:{width:0,lineCap:"square"},plotOptions:{bar:{dataLabels:{position:"center"}}},dataLabels:{enabled:!1,formatter:function(t,e){e.ctx;var i=e.seriesIndex,a=e.dataPointIndex,s=e.w,r=s.globals.seriesRangeStart[i][a];return s.globals.seriesRangeEnd[i][a]-r},background:{enabled:!1},style:{colors:["#fff"]}},tooltip:{shared:!1,followCursor:!0,custom:function(t){return t.w.config.plotOptions&&t.w.config.plotOptions.bar&&t.w.config.plotOptions.bar.horizontal?function(t){var e=new D(t.ctx,null),i=e.getTooltipValues(t),a=i.color,s=i.seriesName,r=i.ylabel,n=i.startVal,o=i.endVal;return e.buildCustomTooltipHTML({color:a,seriesName:s,ylabel:r,start:n,end:o})}(t):function(t){var e=new D(t.ctx,null),i=e.getTooltipValues(t),a=i.color,s=i.seriesName,r=i.ylabel,n=i.start,o=i.end;return e.buildCustomTooltipHTML({color:a,seriesName:s,ylabel:r,start:n,end:o})}(t)}},xaxis:{tickPlacement:"between",tooltip:{enabled:!1},crosshairs:{stroke:{width:0}}}}}},{key:"area",value:function(){return{stroke:{width:4},fill:{type:"gradient",gradient:{inverseColors:!1,shade:"light",type:"vertical",opacityFrom:.65,opacityTo:.5,stops:[0,100,100]}},markers:{size:0,hover:{sizeOffset:6}},tooltip:{followCursor:!1}}}},{key:"brush",value:function(t){return f.extend(t,{chart:{toolbar:{autoSelected:"selection",show:!1},zoom:{enabled:!1}},dataLabels:{enabled:!1},stroke:{width:1},tooltip:{enabled:!1},xaxis:{tooltip:{enabled:!1}}})}},{key:"stacked100",value:function(t){t.dataLabels=t.dataLabels||{},t.dataLabels.formatter=t.dataLabels.formatter||void 0;var e=t.dataLabels.formatter;return t.yaxis.forEach((function(e,i){t.yaxis[i].min=0,t.yaxis[i].max=100})),"bar"===t.chart.type&&(t.dataLabels.formatter=e||function(t){return"number"==typeof t&&t?t.toFixed(0)+"%":t}),t}},{key:"convertCatToNumeric",value:function(t){return t.xaxis.convertedCatToNumeric=!0,t}},{key:"convertCatToNumericXaxis",value:function(t,e,i){t.xaxis.type="numeric",t.xaxis.labels=t.xaxis.labels||{},t.xaxis.labels.formatter=t.xaxis.labels.formatter||function(t){return f.isNumber(t)?Math.floor(t):t};var a=t.xaxis.labels.formatter,s=t.xaxis.categories&&t.xaxis.categories.length?t.xaxis.categories:t.labels;return i&&i.length&&(s=i.map((function(t){return t.toString()}))),s&&s.length&&(t.xaxis.labels.formatter=function(t){return f.isNumber(t)?a(s[Math.floor(t)-1]):a(t)}),t.xaxis.categories=[],t.labels=[],t.xaxis.tickAmount=t.xaxis.tickAmount||"dataPoints",t}},{key:"bubble",value:function(){return{dataLabels:{style:{colors:["#fff"]}},tooltip:{shared:!1,intersect:!0},xaxis:{crosshairs:{width:0}},fill:{type:"solid",gradient:{shade:"light",inverse:!0,shadeIntensity:.55,opacityFrom:.4,opacityTo:.8}}}}},{key:"scatter",value:function(){return{dataLabels:{enabled:!1},tooltip:{shared:!1,intersect:!0},markers:{size:6,strokeWidth:1,hover:{sizeOffset:2}}}}},{key:"heatmap",value:function(){return{chart:{stacked:!1},fill:{opacity:1},dataLabels:{style:{colors:["#fff"]}},stroke:{colors:["#fff"]},tooltip:{followCursor:!0,marker:{show:!1},x:{show:!1}},legend:{position:"top",markers:{shape:"square",size:10,offsetY:2}},grid:{padding:{right:20}}}}},{key:"pie",value:function(){return{chart:{toolbar:{show:!1}},plotOptions:{pie:{donut:{labels:{show:!1}}}},dataLabels:{formatter:function(t){return t.toFixed(1)+"%"},style:{colors:["#fff"]},background:{enabled:!1},dropShadow:{enabled:!0}},stroke:{colors:["#fff"]},fill:{opacity:1,gradient:{shade:"light",stops:[0,100]}},tooltip:{theme:"dark",fillSeriesColor:!0},legend:{position:"right"}}}},{key:"donut",value:function(){return{chart:{toolbar:{show:!1}},dataLabels:{formatter:function(t){return t.toFixed(1)+"%"},style:{colors:["#fff"]},background:{enabled:!1},dropShadow:{enabled:!0}},stroke:{colors:["#fff"]},fill:{opacity:1,gradient:{shade:"light",shadeIntensity:.35,stops:[80,100],opacityFrom:1,opacityTo:1}},tooltip:{theme:"dark",fillSeriesColor:!0},legend:{position:"right"}}}},{key:"polarArea",value:function(){return this.opts.yaxis[0].tickAmount=this.opts.yaxis[0].tickAmount?this.opts.yaxis[0].tickAmount:6,{chart:{toolbar:{show:!1}},dataLabels:{formatter:function(t){return t.toFixed(1)+"%"},enabled:!1},stroke:{show:!0,width:2},fill:{opacity:.7},tooltip:{theme:"dark",fillSeriesColor:!0},legend:{position:"right"}}}},{key:"radar",value:function(){return this.opts.yaxis[0].labels.offsetY=this.opts.yaxis[0].labels.offsetY?this.opts.yaxis[0].labels.offsetY:6,{dataLabels:{enabled:!1,style:{fontSize:"11px"}},stroke:{width:2},markers:{size:3,strokeWidth:1,strokeOpacity:1},fill:{opacity:.2},tooltip:{shared:!1,intersect:!0,followCursor:!0},grid:{show:!1},xaxis:{labels:{formatter:function(t){return t},style:{colors:["#a8a8a8"],fontSize:"11px"}},tooltip:{enabled:!1},crosshairs:{show:!1}}}}},{key:"radialBar",value:function(){return{chart:{animations:{dynamicAnimation:{enabled:!0,speed:800}},toolbar:{show:!1}},fill:{gradient:{shade:"dark",shadeIntensity:.4,inverseColors:!1,type:"diagonal2",opacityFrom:1,opacityTo:1,stops:[70,98,100]}},legend:{show:!1,position:"right"},tooltip:{enabled:!1,fillSeriesColor:!0}}}}]),t}(),N=function(){function i(t){e(this,i),this.opts=t}return a(i,[{key:"init",value:function(e){var i=e.responsiveOverride,a=this.opts,s=new L,r=new H(a);this.chartType=a.chart.type,"histogram"===this.chartType&&(a.chart.type="bar",a=f.extend({plotOptions:{bar:{columnWidth:"99.99%"}}},a)),a=this.extendYAxis(a),a=this.extendAnnotations(a);var n=s.init(),o={};if(a&&"object"===t(a)){var l={};l=-1!==["line","area","bar","candlestick","rangeBar","histogram","bubble","scatter","heatmap","pie","polarArea","donut","radar","radialBar"].indexOf(a.chart.type)?r[a.chart.type]():r.line(),a.chart.brush&&a.chart.brush.enabled&&(l=r.brush(l)),a.chart.stacked&&"100%"===a.chart.stackType&&(a=r.stacked100(a)),this.checkForDarkTheme(window.Apex),this.checkForDarkTheme(a),a.xaxis=a.xaxis||window.Apex.xaxis||{},i||(a.xaxis.convertedCatToNumeric=!1),((a=this.checkForCatToNumericXAxis(this.chartType,l,a)).chart.sparkline&&a.chart.sparkline.enabled||window.Apex.chart&&window.Apex.chart.sparkline&&window.Apex.chart.sparkline.enabled)&&(l=r.sparkline(l)),o=f.extend(n,l)}var h=f.extend(o,window.Apex);return n=f.extend(h,a),n=this.handleUserInputErrors(n)}},{key:"checkForCatToNumericXAxis",value:function(t,e,i){var a=new H(i),s="bar"===t&&i.plotOptions&&i.plotOptions.bar&&i.plotOptions.bar.horizontal,r="pie"===t||"polarArea"===t||"donut"===t||"radar"===t||"radialBar"===t||"heatmap"===t,n="datetime"!==i.xaxis.type&&"numeric"!==i.xaxis.type,o=i.xaxis.tickPlacement?i.xaxis.tickPlacement:e.xaxis&&e.xaxis.tickPlacement;return s||r||!n||"between"===o||(i=a.convertCatToNumeric(i)),i}},{key:"extendYAxis",value:function(t,e){var i=new L;(void 0===t.yaxis||!t.yaxis||Array.isArray(t.yaxis)&&0===t.yaxis.length)&&(t.yaxis={}),t.yaxis.constructor!==Array&&window.Apex.yaxis&&window.Apex.yaxis.constructor!==Array&&(t.yaxis=f.extend(t.yaxis,window.Apex.yaxis)),t.yaxis.constructor!==Array?t.yaxis=[f.extend(i.yAxis,t.yaxis)]:t.yaxis=f.extendArray(t.yaxis,i.yAxis);var a=!1;t.yaxis.forEach((function(t){t.logarithmic&&(a=!0)}));var s=t.series;return e&&!s&&(s=e.config.series),a&&s.length!==t.yaxis.length&&s.length&&(t.yaxis=s.map((function(e,a){if(e.name||(s[a].name="series-".concat(a+1)),t.yaxis[a])return t.yaxis[a].seriesName=s[a].name,t.yaxis[a];var r=f.extend(i.yAxis,t.yaxis[0]);return r.show=!1,r}))),a&&s.length>1&&s.length!==t.yaxis.length&&console.warn("A multi-series logarithmic chart should have equal number of series and y-axes. Please make sure to equalize both."),t}},{key:"extendAnnotations",value:function(t){return void 0===t.annotations&&(t.annotations={},t.annotations.yaxis=[],t.annotations.xaxis=[],t.annotations.points=[]),t=this.extendYAxisAnnotations(t),t=this.extendXAxisAnnotations(t),t=this.extendPointAnnotations(t)}},{key:"extendYAxisAnnotations",value:function(t){var e=new L;return t.annotations.yaxis=f.extendArray(void 0!==t.annotations.yaxis?t.annotations.yaxis:[],e.yAxisAnnotation),t}},{key:"extendXAxisAnnotations",value:function(t){var e=new L;return t.annotations.xaxis=f.extendArray(void 0!==t.annotations.xaxis?t.annotations.xaxis:[],e.xAxisAnnotation),t}},{key:"extendPointAnnotations",value:function(t){var e=new L;return t.annotations.points=f.extendArray(void 0!==t.annotations.points?t.annotations.points:[],e.pointAnnotation),t}},{key:"checkForDarkTheme",value:function(t){t.theme&&"dark"===t.theme.mode&&(t.tooltip||(t.tooltip={}),"light"!==t.tooltip.theme&&(t.tooltip.theme="dark"),t.chart.foreColor||(t.chart.foreColor="#f6f7f8"),t.chart.background||(t.chart.background="#424242"),t.theme.palette||(t.theme.palette="palette4"))}},{key:"handleUserInputErrors",value:function(t){var e=t;if(e.tooltip.shared&&e.tooltip.intersect)throw new Error("tooltip.shared cannot be enabled when tooltip.intersect is true. Turn off any other option by setting it to false.");if(("bar"===e.chart.type||"rangeBar"===e.chart.type)&&e.plotOptions.bar.horizontal){if(e.yaxis.length>1)throw new Error("Multiple Y Axis for bars are not supported. Switch to column chart by setting plotOptions.bar.horizontal=false");e.yaxis[0].reversed&&(e.yaxis[0].opposite=!0),e.xaxis.tooltip.enabled=!1,e.yaxis[0].tooltip.enabled=!1,e.chart.zoom.enabled=!1}return"bar"!==e.chart.type&&"rangeBar"!==e.chart.type||e.tooltip.shared&&("barWidth"===e.xaxis.crosshairs.width&&e.series.length>1&&(console.warn('crosshairs.width = "barWidth" is only supported in single series, not in a multi-series barChart.'),e.xaxis.crosshairs.width="tickWidth"),e.plotOptions.bar.horizontal&&(e.states.hover.type="none",e.tooltip.shared=!1),e.tooltip.followCursor||(console.warn("followCursor option in shared columns cannot be turned off. Please set %ctooltip.followCursor: true","color: blue;"),e.tooltip.followCursor=!0)),"candlestick"===e.chart.type&&e.yaxis[0].reversed&&(console.warn("Reversed y-axis in candlestick chart is not supported."),e.yaxis[0].reversed=!1),e.chart.group&&0===e.yaxis[0].labels.minWidth&&console.warn("It looks like you have multiple charts in synchronization. You must provide yaxis.labels.minWidth which must be EQUAL for all grouped charts to prevent incorrect behaviour."),Array.isArray(e.stroke.width)&&"line"!==e.chart.type&&"area"!==e.chart.type&&(console.warn("stroke.width option accepts array only for line and area charts. Reverted back to Number"),e.stroke.width=e.stroke.width[0]),e}}]),i}(),O=function(){function t(){e(this,t)}return a(t,[{key:"initGlobalVars",value:function(t){t.series=[],t.seriesCandleO=[],t.seriesCandleH=[],t.seriesCandleL=[],t.seriesCandleC=[],t.seriesRangeStart=[],t.seriesRangeEnd=[],t.seriesRangeBarTimeline=[],t.seriesPercent=[],t.seriesX=[],t.seriesZ=[],t.seriesNames=[],t.seriesTotals=[],t.seriesLog=[],t.stackedSeriesTotals=[],t.seriesXvalues=[],t.seriesYvalues=[],t.labels=[],t.categoryLabels=[],t.timescaleLabels=[],t.noLabelsProvided=!1,t.resizeTimer=null,t.selectionResizeTimer=null,t.delayedElements=[],t.pointsArray=[],t.dataLabelsRects=[],t.isXNumeric=!1,t.xaxisLabelsCount=0,t.skipLastTimelinelabel=!1,t.skipFirstTimelinelabel=!1,t.isDataXYZ=!1,t.isMultiLineX=!1,t.isMultipleYAxis=!1,t.maxY=-Number.MAX_VALUE,t.minY=Number.MIN_VALUE,t.minYArr=[],t.maxYArr=[],t.maxX=-Number.MAX_VALUE,t.minX=Number.MAX_VALUE,t.initialMaxX=-Number.MAX_VALUE,t.initialMinX=Number.MAX_VALUE,t.maxDate=0,t.minDate=Number.MAX_VALUE,t.minZ=Number.MAX_VALUE,t.maxZ=-Number.MAX_VALUE,t.minXDiff=Number.MAX_VALUE,t.yAxisScale=[],t.xAxisScale=null,t.xAxisTicksPositions=[],t.yLabelsCoords=[],t.yTitleCoords=[],t.barPadForNumericAxis=0,t.padHorizontal=0,t.xRange=0,t.yRange=[],t.zRange=0,t.dataPoints=0,t.xTickAmount=0}},{key:"globalVars",value:function(t){return{chartID:null,cuid:null,events:{beforeMount:[],mounted:[],updated:[],clicked:[],selection:[],dataPointSelection:[],zoomed:[],scrolled:[]},colors:[],clientX:null,clientY:null,fill:{colors:[]},stroke:{colors:[]},dataLabels:{style:{colors:[]}},radarPolygons:{fill:{colors:[]}},markers:{colors:[],size:t.markers.size,largestSize:0},animationEnded:!1,isTouchDevice:"ontouchstart"in window||navigator.msMaxTouchPoints,isDirty:!1,isExecCalled:!1,initialConfig:null,initialSeries:[],lastXAxis:[],lastYAxis:[],columnSeries:null,labels:[],timescaleLabels:[],noLabelsProvided:!1,allSeriesCollapsed:!1,collapsedSeries:[],collapsedSeriesIndices:[],ancillaryCollapsedSeries:[],ancillaryCollapsedSeriesIndices:[],risingSeries:[],dataFormatXNumeric:!1,capturedSeriesIndex:-1,capturedDataPointIndex:-1,selectedDataPoints:[],goldenPadding:35,invalidLogScale:!1,ignoreYAxisIndexes:[],yAxisSameScaleIndices:[],maxValsInArrayIndex:0,radialSize:0,selection:void 0,zoomEnabled:"zoom"===t.chart.toolbar.autoSelected&&t.chart.toolbar.tools.zoom&&t.chart.zoom.enabled,panEnabled:"pan"===t.chart.toolbar.autoSelected&&t.chart.toolbar.tools.pan,selectionEnabled:"selection"===t.chart.toolbar.autoSelected&&t.chart.toolbar.tools.selection,yaxis:null,mousedown:!1,lastClientPosition:{},visibleXRange:void 0,yValueDecimal:0,total:0,SVGNS:"http://www.w3.org/2000/svg",svgWidth:0,svgHeight:0,noData:!1,locale:{},dom:{},memory:{methodsToExec:[]},shouldAnimate:!0,skipLastTimelinelabel:!1,skipFirstTimelinelabel:!1,delayedElements:[],axisCharts:!0,isDataXYZ:!1,resized:!1,resizeTimer:null,comboCharts:!1,dataChanged:!1,previousPaths:[],allSeriesHasEqualX:!0,pointsArray:[],dataLabelsRects:[],lastDrawnDataLabelsIndexes:[],hasNullValues:!1,easing:null,zoomed:!1,gridWidth:0,gridHeight:0,rotateXLabels:!1,defaultLabels:!1,xLabelFormatter:void 0,yLabelFormatters:[],xaxisTooltipFormatter:void 0,ttKeyFormatter:void 0,ttVal:void 0,ttZFormatter:void 0,LINE_HEIGHT_RATIO:1.618,xAxisLabelsHeight:0,xAxisLabelsWidth:0,yAxisLabelsWidth:0,scaleX:1,scaleY:1,translateX:0,translateY:0,translateYAxisX:[],yAxisWidths:[],translateXAxisY:0,translateXAxisX:0,tooltip:null}}},{key:"init",value:function(t){var e=this.globalVars(t);return this.initGlobalVars(e),e.initialConfig=f.extend({},t),e.initialSeries=f.clone(t.series),e.lastXAxis=JSON.parse(JSON.stringify(e.initialConfig.xaxis)),e.lastYAxis=JSON.parse(JSON.stringify(e.initialConfig.yaxis)),e}}]),t}(),W=function(){function t(i){e(this,t),this.opts=i}return a(t,[{key:"init",value:function(){var t=new N(this.opts).init({responsiveOverride:!1});return{config:t,globals:(new O).init(t)}}}]),t}(),B=function(){function t(i){e(this,t),this.ctx=i,this.w=i.w,this.twoDSeries=[],this.threeDSeries=[],this.twoDSeriesX=[],this.coreUtils=new y(this.ctx)}return a(t,[{key:"isMultiFormat",value:function(){return this.isFormatXY()||this.isFormat2DArray()}},{key:"isFormatXY",value:function(){var t=this.w.config.series.slice(),e=new X(this.ctx);if(this.activeSeriesIndex=e.getActiveConfigSeriesIndex(),void 0!==t[this.activeSeriesIndex].data&&t[this.activeSeriesIndex].data.length>0&&null!==t[this.activeSeriesIndex].data[0]&&void 0!==t[this.activeSeriesIndex].data[0].x&&null!==t[this.activeSeriesIndex].data[0])return!0}},{key:"isFormat2DArray",value:function(){var t=this.w.config.series.slice(),e=new X(this.ctx);if(this.activeSeriesIndex=e.getActiveConfigSeriesIndex(),void 0!==t[this.activeSeriesIndex].data&&t[this.activeSeriesIndex].data.length>0&&void 0!==t[this.activeSeriesIndex].data[0]&&null!==t[this.activeSeriesIndex].data[0]&&t[this.activeSeriesIndex].data[0].constructor===Array)return!0}},{key:"handleFormat2DArray",value:function(t,e){var i=this.w.config,a=this.w.globals;i.xaxis.sorted&&("datetime"===i.xaxis.type?t[e].data.sort((function(t,e){return new Date(t[0]).getTime()-new Date(e[0]).getTime()})):"numeric"===i.xaxis.type&&t[e].data.sort((function(t,e){return t[0]-e[0]})));for(var s=0;s<t[e].data.length;s++)if(void 0!==t[e].data[s][1]&&(Array.isArray(t[e].data[s][1])&&4===t[e].data[s][1].length?this.twoDSeries.push(f.parseNumber(t[e].data[s][1][3])):5===t[e].data[s].length?this.twoDSeries.push(f.parseNumber(t[e].data[s][4])):this.twoDSeries.push(f.parseNumber(t[e].data[s][1])),a.dataFormatXNumeric=!0),"datetime"===i.xaxis.type){var r=new Date(t[e].data[s][0]);r=new Date(r).getTime(),this.twoDSeriesX.push(r)}else this.twoDSeriesX.push(t[e].data[s][0]);for(var n=0;n<t[e].data.length;n++)void 0!==t[e].data[n][2]&&(this.threeDSeries.push(t[e].data[n][2]),a.isDataXYZ=!0)}},{key:"handleFormatXY",value:function(t,e){var i=this.w.config,a=this.w.globals,s=new R(this.ctx),r=e;a.collapsedSeriesIndices.indexOf(e)>-1&&(r=this.activeSeriesIndex),i.xaxis.sorted&&("datetime"===i.xaxis.type?t[e].data.sort((function(t,e){return new Date(t.x).getTime()-new Date(e.x).getTime()})):"numeric"===i.xaxis.type&&t[e].data.sort((function(t,e){return t.x-e.x})));for(var n=0;n<t[e].data.length;n++)void 0!==t[e].data[n].y&&(Array.isArray(t[e].data[n].y)?this.twoDSeries.push(f.parseNumber(t[e].data[n].y[t[e].data[n].y.length-1])):this.twoDSeries.push(f.parseNumber(t[e].data[n].y)));for(var o=0;o<t[r].data.length;o++){var l="string"==typeof t[r].data[o].x,h=Array.isArray(t[r].data[o].x),c=!h&&!!s.isValidDate(t[r].data[o].x.toString());if(l||c)if(l||i.xaxis.convertedCatToNumeric){var d=a.isBarHorizontal&&a.isRangeData;"datetime"!==i.xaxis.type||d?(this.fallbackToCategory=!0,this.twoDSeriesX.push(t[r].data[o].x)):this.twoDSeriesX.push(s.parseDate(t[r].data[o].x))}else"datetime"===i.xaxis.type?this.twoDSeriesX.push(s.parseDate(t[r].data[o].x.toString())):(a.dataFormatXNumeric=!0,a.isXNumeric=!0,this.twoDSeriesX.push(parseFloat(t[r].data[o].x)));else h?(this.fallbackToCategory=!0,this.twoDSeriesX.push(t[r].data[o].x)):(a.isXNumeric=!0,a.dataFormatXNumeric=!0,this.twoDSeriesX.push(t[r].data[o].x))}if(t[e].data[0]&&void 0!==t[e].data[0].z){for(var g=0;g<t[e].data.length;g++)this.threeDSeries.push(t[e].data[g].z);a.isDataXYZ=!0}}},{key:"handleRangeData",value:function(t,e){var i=this.w.config,a=this.w.globals,s={};return this.isFormat2DArray()?s=this.handleRangeDataFormat("array",t,e):this.isFormatXY()&&(s=this.handleRangeDataFormat("xy",t,e)),a.seriesRangeStart.push(s.start),a.seriesRangeEnd.push(s.end),"datetime"===i.xaxis.type&&a.seriesRangeBarTimeline.push(s.rangeUniques),a.seriesRangeBarTimeline.forEach((function(t,e){t&&t.forEach((function(t,e){t.y.forEach((function(e,i){for(var a=0;a<t.y.length;a++)if(i!==a){var s=e.y1,r=e.y2,n=t.y[a].y1;s<=t.y[a].y2&&n<=r&&(t.overlaps.indexOf(e.rangeName)<0&&t.overlaps.push(e.rangeName),t.overlaps.indexOf(t.y[a].rangeName)<0&&t.overlaps.push(t.y[a].rangeName))}}))}))})),s}},{key:"handleCandleStickData",value:function(t,e){var i=this.w.globals,a={};return this.isFormat2DArray()?a=this.handleCandleStickDataFormat("array",t,e):this.isFormatXY()&&(a=this.handleCandleStickDataFormat("xy",t,e)),i.seriesCandleO[e]=a.o,i.seriesCandleH[e]=a.h,i.seriesCandleL[e]=a.l,i.seriesCandleC[e]=a.c,a}},{key:"handleRangeDataFormat",value:function(t,e,i){var a=[],s=[],r=e[i].data.filter((function(t,e,i){return e===i.findIndex((function(e){return e.x===t.x}))})).map((function(t,e){return{x:t.x,overlaps:[],y:[]}})),n="Please provide [Start, End] values in valid format. Read more https://apexcharts.com/docs/series/#rangecharts",o=new X(this.ctx).getActiveConfigSeriesIndex();if("array"===t){if(2!==e[o].data[0][1].length)throw new Error(n);for(var l=0;l<e[i].data.length;l++)a.push(e[i].data[l][1][0]),s.push(e[i].data[l][1][1])}else if("xy"===t){if(2!==e[o].data[0].y.length)throw new Error(n);for(var h=function(t){var n=f.randomId(),o=e[i].data[t].x,l={y1:e[i].data[t].y[0],y2:e[i].data[t].y[1],rangeName:n};e[i].data[t].rangeName=n;var h=r.findIndex((function(t){return t.x===o}));r[h].y.push(l),a.push(l.y1),s.push(l.y2)},c=0;c<e[i].data.length;c++)h(c)}return{start:a,end:s,rangeUniques:r}}},{key:"handleCandleStickDataFormat",value:function(t,e,i){var a=[],s=[],r=[],n=[],o="Please provide [Open, High, Low and Close] values in valid format. Read more https://apexcharts.com/docs/series/#candlestick";if("array"===t){if(!Array.isArray(e[i].data[0][1])&&5!==e[i].data[0].length||Array.isArray(e[i].data[0][1])&&4!==e[i].data[0][1].length)throw new Error(o);if(5===e[i].data[0].length)for(var l=0;l<e[i].data.length;l++)a.push(e[i].data[l][1]),s.push(e[i].data[l][2]),r.push(e[i].data[l][3]),n.push(e[i].data[l][4]);else for(var h=0;h<e[i].data.length;h++)a.push(e[i].data[h][1][0]),s.push(e[i].data[h][1][1]),r.push(e[i].data[h][1][2]),n.push(e[i].data[h][1][3])}else if("xy"===t){if(4!==e[i].data[0].y.length)throw new Error(o);for(var c=0;c<e[i].data.length;c++)a.push(e[i].data[c].y[0]),s.push(e[i].data[c].y[1]),r.push(e[i].data[c].y[2]),n.push(e[i].data[c].y[3])}return{o:a,h:s,l:r,c:n}}},{key:"parseDataAxisCharts",value:function(t){for(var e=this,i=arguments.length>1&&void 0!==arguments[1]?arguments[1]:this.ctx,a=this.w.config,s=this.w.globals,r=new R(i),n=a.labels.length>0?a.labels.slice():a.xaxis.categories.slice(),o=function(){for(var t=0;t<n.length;t++)if("string"==typeof n[t]){if(!r.isValidDate(n[t]))throw new Error("You have provided invalid Date format. Please provide a valid JavaScript Date");e.twoDSeriesX.push(r.parseDate(n[t]))}else e.twoDSeriesX.push(n[t])},l=0;l<t.length;l++){if(this.twoDSeries=[],this.twoDSeriesX=[],this.threeDSeries=[],void 0===t[l].data)return void console.error("It is a possibility that you may have not included 'data' property in series.");if("rangeBar"!==a.chart.type&&"rangeArea"!==a.chart.type&&"rangeBar"!==t[l].type&&"rangeArea"!==t[l].type||(s.isRangeData=!0,this.handleRangeData(t,l)),this.isMultiFormat())this.isFormat2DArray()?this.handleFormat2DArray(t,l):this.isFormatXY()&&this.handleFormatXY(t,l),"candlestick"!==a.chart.type&&"candlestick"!==t[l].type||this.handleCandleStickData(t,l),s.series.push(this.twoDSeries),s.labels.push(this.twoDSeriesX),s.seriesX.push(this.twoDSeriesX),l!==this.activeSeriesIndex||this.fallbackToCategory||(s.isXNumeric=!0);else{"datetime"===a.xaxis.type?(s.isXNumeric=!0,o(),s.seriesX.push(this.twoDSeriesX)):"numeric"===a.xaxis.type&&(s.isXNumeric=!0,n.length>0&&(this.twoDSeriesX=n,s.seriesX.push(this.twoDSeriesX))),s.labels.push(this.twoDSeriesX);var h=t[l].data.map((function(t){return f.parseNumber(t)}));s.series.push(h)}s.seriesZ.push(this.threeDSeries),void 0!==t[l].name?s.seriesNames.push(t[l].name):s.seriesNames.push("series-"+parseInt(l+1,10))}return this.w}},{key:"parseDataNonAxisCharts",value:function(t){var e=this.w.globals,i=this.w.config;e.series=t.slice(),e.seriesNames=i.labels.slice();for(var a=0;a<e.series.length;a++)void 0===e.seriesNames[a]&&e.seriesNames.push("series-"+(a+1));return this.w}},{key:"handleExternalLabelsData",value:function(t){var e=this.w.config,i=this.w.globals;if(e.xaxis.categories.length>0)i.labels=e.xaxis.categories;else if(e.labels.length>0)i.labels=e.labels.slice();else if(this.fallbackToCategory){if(i.labels=i.labels[0],i.seriesRangeBarTimeline.length&&(i.seriesRangeBarTimeline.map((function(t){t.forEach((function(t){i.labels.indexOf(t.x)<0&&t.x&&i.labels.push(t.x)}))})),i.labels=i.labels.filter((function(t,e,i){return i.indexOf(t)===e}))),e.xaxis.convertedCatToNumeric)new H(e).convertCatToNumericXaxis(e,this.ctx,i.seriesX[0]),this._generateExternalLabels(t)}else this._generateExternalLabels(t)}},{key:"_generateExternalLabels",value:function(t){var e=this.w.globals,i=this.w.config,a=[];if(e.axisCharts){if(e.series.length>0)for(var s=0;s<e.series[e.maxValsInArrayIndex].length;s++)a.push(s+1);e.seriesX=[];for(var r=0;r<t.length;r++)e.seriesX.push(a);e.isXNumeric=!0}if(0===a.length){a=e.axisCharts?[]:e.series.map((function(t,e){return e+1}));for(var n=0;n<t.length;n++)e.seriesX.push(a)}e.labels=a,i.xaxis.convertedCatToNumeric&&(e.categoryLabels=a.map((function(t){return i.xaxis.labels.formatter(t)}))),e.noLabelsProvided=!0}},{key:"parseData",value:function(t){var e=this.w,i=e.config,a=e.globals;if(this.excludeCollapsedSeriesInYAxis(),this.fallbackToCategory=!1,this.ctx.core.resetGlobals(),this.ctx.core.isMultipleY(),a.axisCharts?this.parseDataAxisCharts(t):this.parseDataNonAxisCharts(t),this.coreUtils.getLargestSeries(),"bar"===i.chart.type&&i.chart.stacked){var s=new X(this.ctx);a.series=s.setNullSeriesToZeroValues(a.series)}this.coreUtils.getSeriesTotals(),a.axisCharts&&this.coreUtils.getStackedSeriesTotals(),this.coreUtils.getPercentSeries(),a.dataFormatXNumeric||a.isXNumeric&&("numeric"!==i.xaxis.type||0!==i.labels.length||0!==i.xaxis.categories.length)||this.handleExternalLabelsData(t);for(var r=this.coreUtils.getCategoryLabels(a.labels),n=0;n<r.length;n++)if(Array.isArray(r[n])){a.isMultiLineX=!0;break}}},{key:"excludeCollapsedSeriesInYAxis",value:function(){var t=this,e=this.w;e.globals.ignoreYAxisIndexes=e.globals.collapsedSeries.map((function(i,a){if(t.w.globals.isMultipleYAxis&&!e.config.chart.stacked)return i.index}))}}]),t}(),V=function(){function t(i){e(this,t),this.ctx=i,this.w=i.w,this.tooltipKeyFormat="dd MMM"}return a(t,[{key:"xLabelFormat",value:function(t,e,i){var a=this.w;if("datetime"===a.config.xaxis.type&&void 0===a.config.xaxis.labels.formatter&&void 0===a.config.tooltip.x.formatter){var s=new R(this.ctx);return s.formatDate(s.getDate(e),a.config.tooltip.x.format)}return t(e,i)}},{key:"defaultGeneralFormatter",value:function(t){return Array.isArray(t)?t.map((function(t){return t})):t}},{key:"defaultYFormatter",value:function(t,e,i){var a=this.w;return f.isNumber(t)&&(t=0!==a.globals.yValueDecimal?t.toFixed(void 0!==e.decimalsInFloat?e.decimalsInFloat:a.globals.yValueDecimal):a.globals.maxYArr[i]-a.globals.minYArr[i]<10?t.toFixed(1):t.toFixed(0)),t}},{key:"setLabelFormatters",value:function(){var t=this,e=this.w;return e.globals.xLabelFormatter=function(e){return t.defaultGeneralFormatter(e)},e.globals.xaxisTooltipFormatter=function(e){return t.defaultGeneralFormatter(e)},e.globals.ttKeyFormatter=function(e){return t.defaultGeneralFormatter(e)},e.globals.ttZFormatter=function(t){return t},e.globals.legendFormatter=function(e){return t.defaultGeneralFormatter(e)},void 0!==e.config.xaxis.labels.formatter?e.globals.xLabelFormatter=e.config.xaxis.labels.formatter:e.globals.xLabelFormatter=function(t){if(f.isNumber(t)){if(!e.config.xaxis.convertedCatToNumeric&&"numeric"===e.config.xaxis.type&&e.globals.dataPoints<50)return t.toFixed(1);if(e.globals.isBarHorizontal)if(e.globals.maxY-e.globals.minYArr<4)return t.toFixed(1);return t.toFixed(0)}return t},"function"==typeof e.config.tooltip.x.formatter?e.globals.ttKeyFormatter=e.config.tooltip.x.formatter:e.globals.ttKeyFormatter=e.globals.xLabelFormatter,"function"==typeof e.config.xaxis.tooltip.formatter&&(e.globals.xaxisTooltipFormatter=e.config.xaxis.tooltip.formatter),(Array.isArray(e.config.tooltip.y)||void 0!==e.config.tooltip.y.formatter)&&(e.globals.ttVal=e.config.tooltip.y),void 0!==e.config.tooltip.z.formatter&&(e.globals.ttZFormatter=e.config.tooltip.z.formatter),void 0!==e.config.legend.formatter&&(e.globals.legendFormatter=e.config.legend.formatter),e.config.yaxis.forEach((function(i,a){void 0!==i.labels.formatter?e.globals.yLabelFormatters[a]=i.labels.formatter:e.globals.yLabelFormatters[a]=function(s){return e.globals.xyCharts?Array.isArray(s)?s.map((function(e){return t.defaultYFormatter(e,i,a)})):t.defaultYFormatter(s,i,a):s}})),e.globals}},{key:"heatmapLabelFormatters",value:function(){var t=this.w;if("heatmap"===t.config.chart.type){t.globals.yAxisScale[0].result=t.globals.seriesNames.slice();var e=t.globals.seriesNames.reduce((function(t,e){return t.length>e.length?t:e}),0);t.globals.yAxisScale[0].niceMax=e,t.globals.yAxisScale[0].niceMin=e}}}]),t}(),G=function(){function t(i){e(this,t),this.ctx=i,this.w=i.w}return a(t,[{key:"getLabel",value:function(t,e,i,a){var s=arguments.length>4&&void 0!==arguments[4]?arguments[4]:[],r=arguments.length>5&&void 0!==arguments[5]?arguments[5]:"12px",n=this.w,o=void 0===t[a]?"":t[a],l=o,h=n.globals.xLabelFormatter,c=n.config.xaxis.labels.formatter,d=!1,g=new V(this.ctx),u=o;l=g.xLabelFormat(h,o,u),void 0!==c&&(l=c(o,t[a],a));var f=function(t){var i=null;return e.forEach((function(t){"month"===t.unit?i="year":"day"===t.unit?i="month":"hour"===t.unit?i="day":"minute"===t.unit&&(i="hour")})),i===t};e.length>0?(d=f(e[a].unit),i=e[a].position,l=e[a].value):"datetime"===n.config.xaxis.type&&void 0===c&&(l=""),void 0===l&&(l=""),l=Array.isArray(l)?l:l.toString();var p=new b(this.ctx),x={};return x=n.globals.rotateXLabels?p.getTextRects(l,parseInt(r,10),null,"rotate(".concat(n.config.xaxis.labels.rotate," 0 0)"),!1):p.getTextRects(l,parseInt(r,10)),!Array.isArray(l)&&(0===l.indexOf("NaN")||0===l.toLowerCase().indexOf("invalid")||l.toLowerCase().indexOf("infinity")>=0||s.indexOf(l)>=0&&!n.config.xaxis.labels.showDuplicates)&&(l=""),{x:i,text:l,textRect:x,isBold:d}}},{key:"checkForOverflowingLabels",value:function(t,e,i,a,s){var r=this.w;if(0===t&&r.globals.skipFirstTimelinelabel&&(e.text=""),t===i-1&&r.globals.skipLastTimelinelabel&&(e.text=""),r.config.xaxis.labels.hideOverlappingLabels&&a.length>0){var n=s[s.length-1];e.x<n.textRect.width/(r.globals.rotateXLabels?Math.abs(r.config.xaxis.labels.rotate)/12:1.01)+n.x&&(e.text="")}return e}},{key:"checkForReversedLabels",value:function(t,e){var i=this.w;return i.config.yaxis[t]&&i.config.yaxis[t].reversed&&e.reverse(),e}},{key:"isYAxisHidden",value:function(t){var e=this.w,i=new y(this.ctx);return!e.config.yaxis[t].show||!e.config.yaxis[t].showForNullSeries&&i.isSeriesNull(t)&&-1===e.globals.collapsedSeriesIndices.indexOf(t)}},{key:"drawYAxisTicks",value:function(t,e,i,a,s,r,n){var o=this.w,l=new b(this.ctx),h=o.globals.translateY;if(a.show&&e>0){!0===o.config.yaxis[s].opposite&&(t+=a.width);for(var c=e;c>=0;c--){var d=h+e/10+o.config.yaxis[s].labels.offsetY-1;o.globals.isBarHorizontal&&(d=r*c),"heatmap"===o.config.chart.type&&(d+=r/2);var g=l.drawLine(t+i.offsetX-a.width+a.offsetX,d+a.offsetY,t+i.offsetX+a.offsetX,d+a.offsetY,a.color);n.add(g),h+=r}}}}]),t}(),_=function(){function t(i){e(this,t),this.ctx=i,this.w=i.w}return a(t,[{key:"fixSvgStringForIe11",value:function(t){if(!f.isIE11())return t;var e=0,i=t.replace(/xmlns="http:\/\/www.w3.org\/2000\/svg"/g,(function(t){return 2===++e?'xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.com/svgjs"':t}));return i=(i=i.replace(/xmlns:NS\d+=""/g,"")).replace(/NS\d+:(\w+:\w+=")/g,"$1")}},{key:"getSvgString",value:function(){var t=this.w.globals.dom.Paper.svg();return this.fixSvgStringForIe11(t)}},{key:"cleanup",value:function(){var t=this.w,e=t.globals.dom.baseEl.getElementsByClassName("apexcharts-xcrosshairs"),i=t.globals.dom.baseEl.getElementsByClassName("apexcharts-ycrosshairs"),a=t.globals.dom.baseEl.querySelectorAll(".apexcharts-zoom-rect, .apexcharts-selection-rect");Array.prototype.forEach.call(a,(function(t){t.setAttribute("width",0)})),e&&e[0]&&(e[0].setAttribute("x",-500),e[0].setAttribute("x1",-500),e[0].setAttribute("x2",-500)),i&&i[0]&&(i[0].setAttribute("y",-100),i[0].setAttribute("y1",-100),i[0].setAttribute("y2",-100))}},{key:"svgUrl",value:function(){this.cleanup();var t=this.getSvgString(),e=new Blob([t],{type:"image/svg+xml;charset=utf-8"});return URL.createObjectURL(e)}},{key:"dataURI",value:function(){var t=this;return new Promise((function(e){var i=t.w;t.cleanup();var a=document.createElement("canvas");a.width=i.globals.svgWidth,a.height=parseInt(i.globals.dom.elWrap.style.height,10);var s="transparent"===i.config.chart.background?"#fff":i.config.chart.background,r=a.getContext("2d");r.fillStyle=s,r.fillRect(0,0,a.width,a.height);var n=t.getSvgString();if(window.canvg&&f.isIE11()){var o=window.canvg.Canvg.fromString(r,n,{ignoreClear:!0,ignoreDimensions:!0});o.start();var l=a.msToBlob();o.stop(),e({blob:l})}else{var h="data:image/svg+xml,"+encodeURIComponent(n),c=new Image;c.crossOrigin="anonymous",c.onload=function(){if(r.drawImage(c,0,0),a.msToBlob){var t=a.msToBlob();e({blob:t})}else{var i=a.toDataURL("image/png");e({imgURI:i})}},c.src=h}}))}},{key:"exportToSVG",value:function(){this.triggerDownload(this.svgUrl(),null,".svg")}},{key:"exportToPng",value:function(){var t=this;this.dataURI().then((function(e){var i=e.imgURI,a=e.blob;a?navigator.msSaveOrOpenBlob(a,t.w.globals.chartID+".png"):t.triggerDownload(i,null,".png")}))}},{key:"exportToCSV",value:function(t){var e=this,i=t.series,a=t.columnDelimiter,s=t.lineDelimiter,r=void 0===s?"\n":s,n=this.w,o=[],l=[],h="data:text/csv;charset=utf-8,",c=new B(this.ctx),d=new G(this.ctx),g=function(t){var i="";if(n.globals.axisCharts){if("category"===n.config.xaxis.type||n.config.xaxis.convertedCatToNumeric)if(n.globals.isBarHorizontal){var a=n.globals.yLabelFormatters[0],s=new X(e.ctx).getActiveConfigSeriesIndex();i=a(n.globals.labels[t],{seriesIndex:s,dataPointIndex:t,w:n})}else i=d.getLabel(n.globals.labels,n.globals.timescaleLabels,0,t).text;"datetime"===n.config.xaxis.type&&(n.config.xaxis.categories.length?i=n.config.xaxis.categories[t]:n.config.labels.length&&(i=n.config.labels[t]))}else i=n.config.labels[t];return i};o.push(n.config.chart.toolbar.export.csv.headerCategory),i.map((function(t,e){n.globals.axisCharts&&o.push(t.name?t.name:"series-".concat(e))})),n.globals.axisCharts||(o.push(n.config.chart.toolbar.export.csv.headerValue),l.push(o.join(a))),i.map((function(t,e){n.globals.axisCharts?function(t,e){if(o.length&&0===e&&l.push(o.join(a)),t.data&&t.data.length)for(var s=0;s<t.data.length;s++){o=[];var r=g(s);if(r||(c.isFormatXY()?r=i[e].data[s].x:c.isFormat2DArray()&&(r=i[e].data[s]?i[e].data[s][0]:"")),0===e){o.push((d=r,"datetime"===n.config.xaxis.type&&String(d).length>=10?n.config.chart.toolbar.export.csv.dateFormatter(r):r));for(var h=0;h<n.globals.series.length;h++)o.push(n.globals.series[h][s])}("candlestick"===n.config.chart.type||t.type&&"candlestick"===t.type)&&(o.pop(),o.push(n.globals.seriesCandleO[e][s]),o.push(n.globals.seriesCandleH[e][s]),o.push(n.globals.seriesCandleL[e][s]),o.push(n.globals.seriesCandleC[e][s])),"rangeBar"===n.config.chart.type&&(o.pop(),o.push(n.globals.seriesRangeStart[e][s]),o.push(n.globals.seriesRangeEnd[e][s])),o.length&&l.push(o.join(a))}var d}(t,e):((o=[]).push(n.globals.labels[e]),o.push(n.globals.series[e]),l.push(o.join(a)))})),h+=l.join(r),this.triggerDownload(encodeURI(h),n.config.chart.toolbar.export.csv.filename,".csv")}},{key:"triggerDownload",value:function(t,e,i){var a=document.createElement("a");a.href=t,a.download=(e||this.w.globals.chartID)+i,document.body.appendChild(a),a.click(),document.body.removeChild(a)}}]),t}(),j=function(){function t(i){e(this,t),this.ctx=i,this.w=i.w;var a=this.w;this.axesUtils=new G(i),this.xaxisLabels=a.globals.labels.slice(),a.globals.timescaleLabels.length>0&&!a.globals.isBarHorizontal&&(this.xaxisLabels=a.globals.timescaleLabels.slice()),this.drawnLabels=[],this.drawnLabelsRects=[],"top"===a.config.xaxis.position?this.offY=0:this.offY=a.globals.gridHeight+1,this.offY=this.offY+a.config.xaxis.axisBorder.offsetY,this.isCategoryBarHorizontal="bar"===a.config.chart.type&&a.config.plotOptions.bar.horizontal,this.xaxisFontSize=a.config.xaxis.labels.style.fontSize,this.xaxisFontFamily=a.config.xaxis.labels.style.fontFamily,this.xaxisForeColors=a.config.xaxis.labels.style.colors,this.xaxisBorderWidth=a.config.xaxis.axisBorder.width,this.isCategoryBarHorizontal&&(this.xaxisBorderWidth=a.config.yaxis[0].axisBorder.width.toString()),this.xaxisBorderWidth.indexOf("%")>-1?this.xaxisBorderWidth=a.globals.gridWidth*parseInt(this.xaxisBorderWidth,10)/100:this.xaxisBorderWidth=parseInt(this.xaxisBorderWidth,10),this.xaxisBorderHeight=a.config.xaxis.axisBorder.height,this.yaxis=a.config.yaxis[0]}return a(t,[{key:"drawXaxis",value:function(){var t,e=this,i=this.w,a=new b(this.ctx),s=a.group({class:"apexcharts-xaxis",transform:"translate(".concat(i.config.xaxis.offsetX,", ").concat(i.config.xaxis.offsetY,")")}),r=a.group({class:"apexcharts-xaxis-texts-g",transform:"translate(".concat(i.globals.translateXAxisX,", ").concat(i.globals.translateXAxisY,")")});s.add(r);for(var n=i.globals.padHorizontal,o=[],l=0;l<this.xaxisLabels.length;l++)o.push(this.xaxisLabels[l]);var h=o.length;if(i.globals.isXNumeric){var c=h>1?h-1:h;t=i.globals.gridWidth/c,n=n+t/2+i.config.xaxis.labels.offsetX}else t=i.globals.gridWidth/o.length,n=n+t+i.config.xaxis.labels.offsetX;if(i.config.xaxis.labels.show)for(var d=function(s){var l=n-t/2+i.config.xaxis.labels.offsetX;0===s&&1===h&&t/2===n&&1===i.globals.dataPoints&&(l=i.globals.gridWidth/2);var c=e.axesUtils.getLabel(o,i.globals.timescaleLabels,l,s,e.drawnLabels,e.xaxisFontSize),d=28;i.globals.rotateXLabels&&(d=22);(c=e.axesUtils.checkForOverflowingLabels(s,c,h,e.drawnLabels,e.drawnLabelsRects)).text&&i.globals.xaxisLabelsCount++;var g=a.drawText({x:c.x,y:e.offY+i.config.xaxis.labels.offsetY+d-("top"===i.config.xaxis.position?i.globals.xAxisHeight+i.config.xaxis.axisTicks.height-2:0),text:c.text,textAnchor:"middle",fontWeight:c.isBold?600:i.config.xaxis.labels.style.fontWeight,fontSize:e.xaxisFontSize,fontFamily:e.xaxisFontFamily,foreColor:Array.isArray(e.xaxisForeColors)?i.config.xaxis.convertedCatToNumeric?e.xaxisForeColors[i.globals.minX+s-1]:e.xaxisForeColors[s]:e.xaxisForeColors,isPlainText:!1,cssClass:"apexcharts-xaxis-label "+i.config.xaxis.labels.style.cssClass});r.add(g);var u=document.createElementNS(i.globals.SVGNS,"title");u.textContent=c.text,g.node.appendChild(u),""!==c.text&&(e.drawnLabels.push(c.text),e.drawnLabelsRects.push(c)),n+=t},g=0;g<=h-1;g++)d(g);if(void 0!==i.config.xaxis.title.text){var u=a.group({class:"apexcharts-xaxis-title"}),f=a.drawText({x:i.globals.gridWidth/2+i.config.xaxis.title.offsetX,y:this.offY-parseFloat(this.xaxisFontSize)+i.globals.xAxisLabelsHeight+i.config.xaxis.title.offsetY,text:i.config.xaxis.title.text,textAnchor:"middle",fontSize:i.config.xaxis.title.style.fontSize,fontFamily:i.config.xaxis.title.style.fontFamily,fontWeight:i.config.xaxis.title.style.fontWeight,foreColor:i.config.xaxis.title.style.color,cssClass:"apexcharts-xaxis-title-text "+i.config.xaxis.title.style.cssClass});u.add(f),s.add(u)}if(i.config.xaxis.axisBorder.show){var p=i.globals.barPadForNumericAxis,x=a.drawLine(i.globals.padHorizontal+i.config.xaxis.axisBorder.offsetX-p,this.offY,this.xaxisBorderWidth+p,this.offY,i.config.xaxis.axisBorder.color,0,this.xaxisBorderHeight);s.add(x)}return s}},{key:"drawXaxisInversed",value:function(t){var e,i,a=this.w,s=new b(this.ctx),r=a.config.yaxis[0].opposite?a.globals.translateYAxisX[t]:0,n=s.group({class:"apexcharts-yaxis apexcharts-xaxis-inversed",rel:t}),o=s.group({class:"apexcharts-yaxis-texts-g apexcharts-xaxis-inversed-texts-g",transform:"translate("+r+", 0)"});n.add(o);var l=[];if(a.config.yaxis[t].show)for(var h=0;h<this.xaxisLabels.length;h++)l.push(this.xaxisLabels[h]);i=-(e=a.globals.gridHeight/l.length)/2.2;var c=a.globals.yLabelFormatters[0],d=a.config.yaxis[0].labels;if(d.show)for(var g=0;g<=l.length-1;g++){var u=void 0===l[g]?"":l[g];u=c(u,{seriesIndex:t,dataPointIndex:g,w:a});var f=0;Array.isArray(u)&&(f=u.length/2*parseInt(d.style.fontSize,10));var p=s.drawText({x:d.offsetX-15,y:i+e+d.offsetY-f,text:u,textAnchor:this.yaxis.opposite?"start":"end",foreColor:Array.isArray(d.style.colors)?d.style.colors[g]:d.style.colors,fontSize:d.style.fontSize,fontFamily:d.style.fontFamily,fontWeight:d.style.fontWeight,isPlainText:!1,cssClass:"apexcharts-yaxis-label "+d.style.cssClass});o.add(p);var x=document.createElementNS(a.globals.SVGNS,"title");if(x.textContent=u.text,p.node.appendChild(x),0!==a.config.yaxis[t].labels.rotate){var m=s.rotateAroundCenter(p.node);p.node.setAttribute("transform","rotate(".concat(a.config.yaxis[t].labels.rotate," 0 ").concat(m.y,")"))}i+=e}if(void 0!==a.config.yaxis[0].title.text){var v=s.group({class:"apexcharts-yaxis-title apexcharts-xaxis-title-inversed",transform:"translate("+r+", 0)"}),y=s.drawText({x:0,y:a.globals.gridHeight/2,text:a.config.yaxis[0].title.text,textAnchor:"middle",foreColor:a.config.yaxis[0].title.style.color,fontSize:a.config.yaxis[0].title.style.fontSize,fontWeight:a.config.yaxis[0].title.style.fontWeight,fontFamily:a.config.yaxis[0].title.style.fontFamily,cssClass:"apexcharts-yaxis-title-text "+a.config.yaxis[0].title.style.cssClass});v.add(y),n.add(v)}var w=0;this.isCategoryBarHorizontal&&a.config.yaxis[0].opposite&&(w=a.globals.gridWidth);var k=a.config.xaxis.axisBorder;if(k.show){var A=s.drawLine(a.globals.padHorizontal+k.offsetX+w,1+k.offsetY,a.globals.padHorizontal+k.offsetX+w,a.globals.gridHeight+k.offsetY,k.color,0);n.add(A)}return a.config.yaxis[0].axisTicks.show&&this.axesUtils.drawYAxisTicks(w,l.length,a.config.yaxis[0].axisBorder,a.config.yaxis[0].axisTicks,0,e,n),n}},{key:"drawXaxisTicks",value:function(t,e){var i=this.w,a=t;if(!(t<0||t-2>i.globals.gridWidth)){var s=this.offY+i.config.xaxis.axisTicks.offsetY,r=s+i.config.xaxis.axisTicks.height;if("top"===i.config.xaxis.position&&(r=s-i.config.xaxis.axisTicks.height),i.config.xaxis.axisTicks.show){var n=new b(this.ctx).drawLine(t+i.config.xaxis.axisTicks.offsetX,s+i.config.xaxis.offsetY,a+i.config.xaxis.axisTicks.offsetX,r+i.config.xaxis.offsetY,i.config.xaxis.axisTicks.color);e.add(n),n.node.classList.add("apexcharts-xaxis-tick")}}}},{key:"getXAxisTicksPositions",value:function(){var t=this.w,e=[],i=this.xaxisLabels.length,a=t.globals.padHorizontal;if(t.globals.timescaleLabels.length>0)for(var s=0;s<i;s++)a=this.xaxisLabels[s].position,e.push(a);else for(var r=i,n=0;n<r;n++){var o=r;t.globals.isXNumeric&&"bar"!==t.config.chart.type&&(o-=1),a+=t.globals.gridWidth/o,e.push(a)}return e}},{key:"xAxisLabelCorrections",value:function(){var t=this.w,e=new b(this.ctx),i=t.globals.dom.baseEl.querySelector(".apexcharts-xaxis-texts-g"),a=t.globals.dom.baseEl.querySelectorAll(".apexcharts-xaxis-texts-g text"),s=t.globals.dom.baseEl.querySelectorAll(".apexcharts-yaxis-inversed text"),r=t.globals.dom.baseEl.querySelectorAll(".apexcharts-xaxis-inversed-texts-g text tspan");if(t.globals.rotateXLabels||t.config.xaxis.labels.rotateAlways)for(var n=0;n<a.length;n++){var o=e.rotateAroundCenter(a[n]);o.y=o.y-1,o.x=o.x+1,a[n].setAttribute("transform","rotate(".concat(t.config.xaxis.labels.rotate," ").concat(o.x," ").concat(o.y,")")),a[n].setAttribute("text-anchor","end");i.setAttribute("transform","translate(0, ".concat(-10,")"));var l=a[n].childNodes;t.config.xaxis.labels.trim&&Array.prototype.forEach.call(l,(function(i){e.placeTextWithEllipsis(i,i.textContent,t.config.xaxis.labels.maxHeight-("bottom"===t.config.legend.position?20:10))}))}else!function(){for(var i=t.globals.gridWidth/(t.globals.labels.length+1),s=0;s<a.length;s++){var r=a[s].childNodes;t.config.xaxis.labels.trim&&"datetime"!==t.config.xaxis.type&&Array.prototype.forEach.call(r,(function(t){e.placeTextWithEllipsis(t,t.textContent,i)}))}}();if(s.length>0){var h=s[s.length-1].getBBox(),c=s[0].getBBox();h.x<-20&&s[s.length-1].parentNode.removeChild(s[s.length-1]),c.x+c.width>t.globals.gridWidth&&!t.globals.isBarHorizontal&&s[0].parentNode.removeChild(s[0]);for(var d=0;d<r.length;d++)e.placeTextWithEllipsis(r[d],r[d].textContent,t.config.yaxis[0].labels.maxWidth-2*parseFloat(t.config.yaxis[0].title.style.fontSize)-20)}}}]),t}(),U=function(){function t(i){e(this,t),this.ctx=i,this.w=i.w;var a=this.w;this.xaxisLabels=a.globals.labels.slice(),this.axesUtils=new G(i),this.isTimelineBar="datetime"===a.config.xaxis.type&&a.globals.seriesRangeBarTimeline.length,a.globals.timescaleLabels.length>0&&(this.xaxisLabels=a.globals.timescaleLabels.slice())}return a(t,[{key:"drawGridArea",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null,e=this.w,i=new b(this.ctx);null===t&&(t=i.group({class:"apexcharts-grid"}));var a=i.drawLine(e.globals.padHorizontal,1,e.globals.padHorizontal,e.globals.gridHeight,"transparent"),s=i.drawLine(e.globals.padHorizontal,e.globals.gridHeight,e.globals.gridWidth,e.globals.gridHeight,"transparent");return t.add(s),t.add(a),t}},{key:"drawGrid",value:function(){var t=null;return this.w.globals.axisCharts&&(t=this.renderGrid(),this.drawGridArea(t.el)),t}},{key:"createGridMask",value:function(){var t=this.w,e=t.globals,i=new b(this.ctx),a=Array.isArray(t.config.stroke.width)?0:t.config.stroke.width;if(Array.isArray(t.config.stroke.width)){var s=0;t.config.stroke.width.forEach((function(t){s=Math.max(s,t)})),a=s}e.dom.elGridRectMask=document.createElementNS(e.SVGNS,"clipPath"),e.dom.elGridRectMask.setAttribute("id","gridRectMask".concat(e.cuid)),e.dom.elGridRectMarkerMask=document.createElementNS(e.SVGNS,"clipPath"),e.dom.elGridRectMarkerMask.setAttribute("id","gridRectMarkerMask".concat(e.cuid));var r=t.config.chart.type,n=0,o=0;("bar"===r||"rangeBar"===r||t.globals.comboBarCount>0)&&t.globals.isXNumeric&&!t.globals.isBarHorizontal&&(n=t.config.grid.padding.left,o=t.config.grid.padding.right,e.barPadForNumericAxis>n&&(n=e.barPadForNumericAxis,o=e.barPadForNumericAxis)),e.dom.elGridRect=i.drawRect(-a/2-n-2,-a/2,e.gridWidth+a+o+n+4,e.gridHeight+a,0,"#fff"),new y(this).getLargestMarkerSize();var l=t.globals.markers.largestSize+1;e.dom.elGridRectMarker=i.drawRect(2*-l,2*-l,e.gridWidth+4*l,e.gridHeight+4*l,0,"#fff"),e.dom.elGridRectMask.appendChild(e.dom.elGridRect.node),e.dom.elGridRectMarkerMask.appendChild(e.dom.elGridRectMarker.node);var h=e.dom.baseEl.querySelector("defs");h.appendChild(e.dom.elGridRectMask),h.appendChild(e.dom.elGridRectMarkerMask)}},{key:"_drawGridLines",value:function(t){var e=t.i,i=t.x1,a=t.y1,s=t.x2,r=t.y2,n=t.xCount,o=t.parent,l=this.w;0===e&&l.globals.skipFirstTimelinelabel||e===n-1&&l.globals.skipLastTimelinelabel||"radar"===l.config.chart.type||(l.config.grid.xaxis.lines.show&&this._drawGridLine({x1:i,y1:a,x2:s,y2:r,parent:o}),new j(this.ctx).drawXaxisTicks(i,this.elg))}},{key:"_drawGridLine",value:function(t){var e=t.x1,i=t.y1,a=t.x2,s=t.y2,r=t.parent,n=this.w,o=r.node.classList.contains("apexcharts-gridlines-horizontal"),l=n.config.grid.strokeDashArray,h=n.globals.barPadForNumericAxis,c=new b(this).drawLine(e-(o?h:0),i,a+(o?h:0),s,n.config.grid.borderColor,l);c.node.classList.add("apexcharts-gridline"),r.add(c)}},{key:"_drawGridBandRect",value:function(t){var e=t.c,i=t.x1,a=t.y1,s=t.x2,r=t.y2,n=t.type,o=this.w,l=new b(this.ctx),h=o.globals.barPadForNumericAxis;if("column"!==n||"datetime"!==o.config.xaxis.type){var c=o.config.grid[n].colors[e],d=l.drawRect(i-("row"===n?h:0),a,s+("row"===n?2*h:0),r,0,c,o.config.grid[n].opacity);this.elg.add(d),d.attr("clip-path","url(#gridRectMask".concat(o.globals.cuid,")")),d.node.classList.add("apexcharts-grid-".concat(n))}}},{key:"_drawXYLines",value:function(t){var e=this,i=t.xCount,a=t.tickAmount,s=this.w;if(s.config.grid.xaxis.lines.show||s.config.xaxis.axisTicks.show){var r=s.globals.padHorizontal,n=s.globals.gridHeight;s.globals.timescaleLabels.length?function(t){for(var a=t.xC,s=t.x1,r=t.y1,n=t.x2,o=t.y2,l=0;l<a;l++)s=e.xaxisLabels[l].position,n=e.xaxisLabels[l].position,e._drawGridLines({i:l,x1:s,y1:r,x2:n,y2:o,xCount:i,parent:e.elgridLinesV})}({xC:i,x1:r,y1:0,x2:void 0,y2:n}):(s.globals.isXNumeric&&(i=s.globals.xAxisScale.result.length),s.config.xaxis.convertedCatToNumeric&&(i=s.globals.xaxisLabelsCount),function(t){for(var a=t.xC,r=t.x1,n=t.y1,o=t.x2,l=t.y2,h=0;h<a+(s.globals.isXNumeric?0:1);h++)0===h&&1===a&&1===s.globals.dataPoints&&(o=r=s.globals.gridWidth/2),e._drawGridLines({i:h,x1:r,y1:n,x2:o,y2:l,xCount:i,parent:e.elgridLinesV}),o=r+=s.globals.gridWidth/(s.globals.isXNumeric?a-1:a)}({xC:i,x1:r,y1:0,x2:void 0,y2:n}))}if(s.config.grid.yaxis.lines.show){var o=0,l=0,h=s.globals.gridWidth,c=a+1;this.isTimelineBar&&(c=s.globals.labels.length);for(var d=0;d<c+(this.isTimelineBar?1:0);d++)this._drawGridLine({x1:0,y1:o,x2:h,y2:l,parent:this.elgridLinesH}),l=o+=s.globals.gridHeight/(this.isTimelineBar?c:a)}}},{key:"_drawInvertedXYLines",value:function(t){var e=t.xCount,i=this.w;if(i.config.grid.xaxis.lines.show||i.config.xaxis.axisTicks.show)for(var a,s=i.globals.padHorizontal,r=i.globals.gridHeight,n=0;n<e+1;n++){i.config.grid.xaxis.lines.show&&this._drawGridLine({x1:s,y1:0,x2:a,y2:r,parent:this.elgridLinesV}),new j(this.ctx).drawXaxisTicks(s,this.elg),a=s=s+i.globals.gridWidth/e+.3}if(i.config.grid.yaxis.lines.show)for(var o=0,l=0,h=i.globals.gridWidth,c=0;c<i.globals.dataPoints+1;c++)this._drawGridLine({x1:0,y1:o,x2:h,y2:l,parent:this.elgridLinesH}),l=o+=i.globals.gridHeight/i.globals.dataPoints}},{key:"renderGrid",value:function(){var t=this.w,e=new b(this.ctx);this.elg=e.group({class:"apexcharts-grid"}),this.elgridLinesH=e.group({class:"apexcharts-gridlines-horizontal"}),this.elgridLinesV=e.group({class:"apexcharts-gridlines-vertical"}),this.elg.add(this.elgridLinesH),this.elg.add(this.elgridLinesV),t.config.grid.show||(this.elgridLinesV.hide(),this.elgridLinesH.hide());for(var i,a=t.globals.yAxisScale.length?t.globals.yAxisScale[0].result.length-1:5,s=0;s<t.globals.series.length&&(void 0!==t.globals.yAxisScale[s]&&(a=t.globals.yAxisScale[s].result.length-1),!(a>2));s++);return!t.globals.isBarHorizontal||this.isTimelineBar?(i=this.xaxisLabels.length,this.isTimelineBar&&(a=t.globals.labels.length),this._drawXYLines({xCount:i,tickAmount:a})):(i=a,a=t.globals.xTickAmount,this._drawInvertedXYLines({xCount:i,tickAmount:a})),this.drawGridBands(i,a),{el:this.elg,xAxisTickWidth:t.globals.gridWidth/i}}},{key:"drawGridBands",value:function(t,e){var i=this.w;if(void 0!==i.config.grid.row.colors&&i.config.grid.row.colors.length>0)for(var a=0,s=i.globals.gridHeight/e,r=i.globals.gridWidth,n=0,o=0;n<e;n++,o++)o>=i.config.grid.row.colors.length&&(o=0),this._drawGridBandRect({c:o,x1:0,y1:a,x2:r,y2:s,type:"row"}),a+=i.globals.gridHeight/e;if(void 0!==i.config.grid.column.colors&&i.config.grid.column.colors.length>0)for(var l=i.globals.isBarHorizontal||"category"!==i.config.xaxis.type&&!i.config.xaxis.convertedCatToNumeric?t:t-1,h=i.globals.padHorizontal,c=i.globals.padHorizontal+i.globals.gridWidth/l,d=i.globals.gridHeight,g=0,u=0;g<t;g++,u++)u>=i.config.grid.column.colors.length&&(u=0),this._drawGridBandRect({c:u,x1:h,y1:0,x2:c,y2:d,type:"column"}),h+=i.globals.gridWidth/l}}]),t}(),q=function(){function t(i){e(this,t),this.ctx=i,this.w=i.w}return a(t,[{key:"niceScale",value:function(t,e){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:10,a=arguments.length>3&&void 0!==arguments[3]?arguments[3]:0,s=arguments.length>4?arguments[4]:void 0,r=this.w;if("dataPoints"===i&&(i=r.globals.dataPoints-1),t===Number.MIN_VALUE&&0===e||!f.isNumber(t)&&!f.isNumber(e)||t===Number.MIN_VALUE&&e===-Number.MAX_VALUE){t=0,e=i;var n=this.linearScale(t,e,i);return n}t>e?(console.warn("axis.min cannot be greater than axis.max"),e=t+.1):t===e&&(t=0===t?0:t-.5,e=0===e?2:e+.5);var o=[],l=Math.abs(e-t);l<1&&s&&("candlestick"===r.config.chart.type||"candlestick"===r.config.series[a].type||r.globals.isRangeData)&&(e*=1.01);var h=i+1;h<2?h=2:h>2&&(h-=2);var c=l/h,d=Math.floor(f.log10(c)),g=Math.pow(10,d),u=Math.round(c/g);u<1&&(u=1);var p=u*g,x=p*Math.floor(t/p),b=p*Math.ceil(e/p),m=x;if(s&&l>2){for(;o.push(m),!((m+=p)>b););return{result:o,niceMin:o[0],niceMax:o[o.length-1]}}var v=t;(o=[]).push(v);for(var y=Math.abs(e-t)/i,w=0;w<=i;w++)v+=y,o.push(v);return o[o.length-2]>=e&&o.pop(),{result:o,niceMin:o[0],niceMax:o[o.length-1]}}},{key:"linearScale",value:function(t,e){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:10,a=Math.abs(e-t),s=a/i;i===Number.MAX_VALUE&&(i=10,s=1);for(var r=[],n=t;i>=0;)r.push(n),n+=s,i-=1;return{result:r,niceMin:r[0],niceMax:r[r.length-1]}}},{key:"logarithmicScale",value:function(t,e,i,a){(e<=0||e===Number.MIN_VALUE)&&(e=.01);for(var s=Math.log(e)/Math.log(10),r=Math.log(i)/Math.log(10),n=Math.abs(i-e)/a,o=[],l=e;a>=0;)o.push(l),l+=n,a-=1;var h=o.map((function(t,a){t<=0&&(t=.01);var n=(r-s)/(i-e),o=Math.pow(10,s+n*(t-s));return Math.round(o/f.roundToBase(o,10))*f.roundToBase(o,10)}));return 0===h[0]&&(h[0]=1),{result:h,niceMin:h[0],niceMax:h[h.length-1]}}},{key:"setYScaleForIndex",value:function(t,e,i){var a=this.w.globals,s=this.w.config,r=a.isBarHorizontal?s.xaxis:s.yaxis[t];void 0===a.yAxisScale[t]&&(a.yAxisScale[t]=[]);var n=Math.abs(i-e);if(r.logarithmic&&n<=5&&(a.invalidLogScale=!0),r.logarithmic&&n>5)a.allSeriesCollapsed=!1,a.yAxisScale[t]=this.logarithmicScale(t,e,i,r.tickAmount?r.tickAmount:Math.floor(Math.log10(i)));else if(i!==-Number.MAX_VALUE&&f.isNumber(i))if(a.allSeriesCollapsed=!1,void 0===r.min&&void 0===r.max||r.forceNiceScale){var o=void 0===s.yaxis[t].max&&void 0===s.yaxis[t].min||s.yaxis[t].forceNiceScale;a.yAxisScale[t]=this.niceScale(e,i,r.tickAmount?r.tickAmount:n<5&&n>1?n+1:5,t,o)}else a.yAxisScale[t]=this.linearScale(e,i,r.tickAmount);else a.yAxisScale[t]=this.linearScale(0,5,5)}},{key:"setXScale",value:function(t,e){var i=this.w,a=i.globals,s=i.config.xaxis,r=Math.abs(e-t);return e!==-Number.MAX_VALUE&&f.isNumber(e)?a.xAxisScale=this.niceScale(t,e,s.tickAmount?s.tickAmount:r<5&&r>1?r+1:5,0):a.xAxisScale=this.linearScale(0,5,5),a.xAxisScale}},{key:"setMultipleYScales",value:function(){var t=this,e=this.w.globals,i=this.w.config,a=e.minYArr.concat([]),s=e.maxYArr.concat([]),r=[];i.yaxis.forEach((function(e,n){var o=n;i.series.forEach((function(t,i){t.name===e.seriesName&&(o=i,n!==i?r.push({index:i,similarIndex:n,alreadyExists:!0}):r.push({index:i}))}));var l=a[o],h=s[o];t.setYScaleForIndex(n,l,h)})),this.sameScaleInMultipleAxes(a,s,r)}},{key:"sameScaleInMultipleAxes",value:function(t,e,i){var a=this,s=this.w.config,r=this.w.globals,n=[];i.forEach((function(t){t.alreadyExists&&(void 0===n[t.index]&&(n[t.index]=[]),n[t.index].push(t.index),n[t.index].push(t.similarIndex))})),r.yAxisSameScaleIndices=n,n.forEach((function(t,e){n.forEach((function(i,a){var s,r;e!==a&&(s=t,r=i,s.filter((function(t){return-1!==r.indexOf(t)}))).length>0&&(n[e]=n[e].concat(n[a]))}))}));var o=n.map((function(t){return t.filter((function(e,i){return t.indexOf(e)===i}))})).map((function(t){return t.sort()}));n=n.filter((function(t){return!!t}));var l=o.slice(),h=l.map((function(t){return JSON.stringify(t)}));l=l.filter((function(t,e){return h.indexOf(JSON.stringify(t))===e}));var c=[],d=[];t.forEach((function(t,i){l.forEach((function(a,s){a.indexOf(i)>-1&&(void 0===c[s]&&(c[s]=[],d[s]=[]),c[s].push({key:i,value:t}),d[s].push({key:i,value:e[i]}))}))}));var g=Array.apply(null,Array(l.length)).map(Number.prototype.valueOf,Number.MIN_VALUE),u=Array.apply(null,Array(l.length)).map(Number.prototype.valueOf,-Number.MAX_VALUE);c.forEach((function(t,e){t.forEach((function(t,i){g[e]=Math.min(t.value,g[e])}))})),d.forEach((function(t,e){t.forEach((function(t,i){u[e]=Math.max(t.value,u[e])}))})),t.forEach((function(t,e){d.forEach((function(t,i){var n=g[i],o=u[i];s.chart.stacked&&(o=0,t.forEach((function(t,e){t.value!==-Number.MAX_VALUE&&(o+=t.value),n!==Number.MIN_VALUE&&(n+=c[i][e].value)}))),t.forEach((function(i,l){t[l].key===e&&(void 0!==s.yaxis[e].min&&(n="function"==typeof s.yaxis[e].min?s.yaxis[e].min(r.minY):s.yaxis[e].min),void 0!==s.yaxis[e].max&&(o="function"==typeof s.yaxis[e].max?s.yaxis[e].max(r.maxY):s.yaxis[e].max),a.setYScaleForIndex(e,n,o))}))}))}))}},{key:"autoScaleY",value:function(t,e,i){t||(t=this);var a=t.w;if(a.globals.isMultipleYAxis||a.globals.collapsedSeries.length)return console.warn("autoScaleYaxis is not supported in a multi-yaxis chart."),e;var s=a.globals.seriesX[0],r=a.config.chart.stacked;return e.forEach((function(t,n){for(var o=0,l=0;l<s.length;l++)if(s[l]>=i.xaxis.min){o=l;break}var h,c,d=a.globals.minYArr[n],g=a.globals.maxYArr[n],u=a.globals.stackedSeriesTotals;a.globals.series.forEach((function(n,l){var f=n[o];r?(f=u[o],h=c=f,u.forEach((function(t,e){s[e]<=i.xaxis.max&&s[e]>=i.xaxis.min&&(t>c&&null!==t&&(c=t),n[e]<h&&null!==n[e]&&(h=n[e]))}))):(h=c=f,n.forEach((function(t,e){if(s[e]<=i.xaxis.max&&s[e]>=i.xaxis.min){var r=t,n=t;a.globals.series.forEach((function(i,a){null!==t&&(r=Math.min(i[e],r),n=Math.max(i[e],n))})),n>c&&null!==n&&(c=n),r<h&&null!==r&&(h=r)}}))),void 0===h&&void 0===c&&(h=d,c=g),(c*=c<0?.9:1.1)<0&&c<g&&(c=g),(h*=h<0?1.1:.9)<0&&h>d&&(h=d),e.length>1?(e[l].min=void 0===t.min?h:t.min,e[l].max=void 0===t.max?c:t.max):(e[0].min=void 0===t.min?h:t.min,e[0].max=void 0===t.max?c:t.max)}))})),e}}]),t}(),Z=function(){function t(i){e(this,t),this.ctx=i,this.w=i.w,this.scales=new q(i)}return a(t,[{key:"init",value:function(){this.setYRange(),this.setXRange(),this.setZRange()}},{key:"getMinYMaxY",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:Number.MAX_VALUE,i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:-Number.MAX_VALUE,a=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null,s=this.w.config,r=this.w.globals,n=-Number.MAX_VALUE,o=Number.MIN_VALUE;null===a&&(a=t+1);var l=r.series,h=l,c=l;"candlestick"===s.chart.type?(h=r.seriesCandleL,c=r.seriesCandleH):r.isRangeData&&(h=r.seriesRangeStart,c=r.seriesRangeEnd);for(var d=t;d<a;d++){r.dataPoints=Math.max(r.dataPoints,l[d].length);for(var g=0;g<r.series[d].length;g++){var u=l[d][g];null!==u&&f.isNumber(u)?(n=Math.max(n,c[d][g]),e=Math.min(e,h[d][g]),i=Math.max(i,h[d][g]),"candlestick"===this.w.config.chart.type&&(n=Math.max(n,r.seriesCandleO[d][g]),n=Math.max(n,r.seriesCandleH[d][g]),n=Math.max(n,r.seriesCandleL[d][g]),i=n=Math.max(n,r.seriesCandleC[d][g])),f.isFloat(u)&&(u=f.noExponents(u),r.yValueDecimal=Math.max(r.yValueDecimal,u.toString().split(".")[1].length)),o>h[d][g]&&h[d][g]<0&&(o=h[d][g])):r.hasNullValues=!0}}return"rangeBar"===s.chart.type&&r.seriesRangeStart.length&&r.isBarHorizontal&&"datetime"===s.xaxis.type&&(o=e),"bar"===s.chart.type&&(o<0&&n<0&&(n=0),o===Number.MIN_VALUE&&(o=0)),{minY:o,maxY:n,lowestY:e,highestY:i}}},{key:"setYRange",value:function(){var t=this.w.globals,e=this.w.config;t.maxY=-Number.MAX_VALUE,t.minY=Number.MIN_VALUE;var i=Number.MAX_VALUE;if(t.isMultipleYAxis)for(var a=0;a<t.series.length;a++){var s=this.getMinYMaxY(a,i,null,a+1);t.minYArr.push(s.minY),t.maxYArr.push(s.maxY),i=s.lowestY}var r=this.getMinYMaxY(0,i,null,t.series.length);if(t.minY=r.minY,t.maxY=r.maxY,i=r.lowestY,e.chart.stacked&&this._setStackedMinMax(),("line"===e.chart.type||"area"===e.chart.type||"candlestick"===e.chart.type||"rangeBar"===e.chart.type&&!t.isBarHorizontal)&&t.minY===Number.MIN_VALUE&&i!==-Number.MAX_VALUE&&i!==t.maxY){var n=t.maxY-i;i>=0&&i<=10&&(n=0),t.minY=i-5*n/100,i>0&&t.minY<0&&(t.minY=0),t.maxY=t.maxY+5*n/100}if(e.yaxis.forEach((function(e,i){void 0!==e.max&&("number"==typeof e.max?t.maxYArr[i]=e.max:"function"==typeof e.max&&(t.maxYArr[i]=e.max(t.maxY)),t.maxY=t.maxYArr[i]),void 0!==e.min&&("number"==typeof e.min?t.minYArr[i]=e.min:"function"==typeof e.min&&(t.minYArr[i]=e.min(t.minY)),t.minY=t.minYArr[i])})),t.isBarHorizontal){["min","max"].forEach((function(i){void 0!==e.xaxis[i]&&"number"==typeof e.xaxis[i]&&("min"===i?t.minY=e.xaxis[i]:t.maxY=e.xaxis[i])}))}return t.isMultipleYAxis?(this.scales.setMultipleYScales(),t.minY=i,t.yAxisScale.forEach((function(e,i){t.minYArr[i]=e.niceMin,t.maxYArr[i]=e.niceMax}))):(this.scales.setYScaleForIndex(0,t.minY,t.maxY),t.minY=t.yAxisScale[0].niceMin,t.maxY=t.yAxisScale[0].niceMax,t.minYArr[0]=t.yAxisScale[0].niceMin,t.maxYArr[0]=t.yAxisScale[0].niceMax),{minY:t.minY,maxY:t.maxY,minYArr:t.minYArr,maxYArr:t.maxYArr}}},{key:"setXRange",value:function(){var t=this.w.globals,e=this.w.config,i="numeric"===e.xaxis.type||"datetime"===e.xaxis.type||"category"===e.xaxis.type&&!t.noLabelsProvided||t.noLabelsProvided||t.isXNumeric;if(t.isXNumeric&&function(){for(var e=0;e<t.series.length;e++)if(t.labels[e])for(var i=0;i<t.labels[e].length;i++)null!==t.labels[e][i]&&f.isNumber(t.labels[e][i])&&(t.maxX=Math.max(t.maxX,t.labels[e][i]),t.initialMaxX=Math.max(t.maxX,t.labels[e][i]),t.minX=Math.min(t.minX,t.labels[e][i]),t.initialMinX=Math.min(t.minX,t.labels[e][i]))}(),t.noLabelsProvided&&0===e.xaxis.categories.length&&(t.maxX=t.labels[t.labels.length-1],t.initialMaxX=t.labels[t.labels.length-1],t.minX=1,t.initialMinX=1),t.isXNumeric||t.noLabelsProvided||t.dataFormatXNumeric){var a;if(void 0===e.xaxis.tickAmount?(a=Math.round(t.svgWidth/150),"numeric"===e.xaxis.type&&t.dataPoints<30&&(a=t.dataPoints-1),a>t.dataPoints&&0!==t.dataPoints&&(a=t.dataPoints-1)):"dataPoints"===e.xaxis.tickAmount?(t.series.length>1&&(a=t.series[t.maxValsInArrayIndex].length-1),t.isXNumeric&&(a=t.maxX-t.minX-1)):a=e.xaxis.tickAmount,t.xTickAmount=a,void 0!==e.xaxis.max&&"number"==typeof e.xaxis.max&&(t.maxX=e.xaxis.max),void 0!==e.xaxis.min&&"number"==typeof e.xaxis.min&&(t.minX=e.xaxis.min),void 0!==e.xaxis.range&&(t.minX=t.maxX-e.xaxis.range),t.minX!==Number.MAX_VALUE&&t.maxX!==-Number.MAX_VALUE)if(e.xaxis.convertedCatToNumeric&&!t.dataFormatXNumeric){for(var s=[],r=t.minX-1;r<t.maxX;r++)s.push(r+1);t.xAxisScale={result:s,niceMin:s[0],niceMax:s[s.length-1]}}else t.xAxisScale=this.scales.setXScale(t.minX,t.maxX);else t.xAxisScale=this.scales.linearScale(1,a,a),t.noLabelsProvided&&t.labels.length>0&&(t.xAxisScale=this.scales.linearScale(1,t.labels.length,a-1),t.seriesX=t.labels.slice());i&&(t.labels=t.xAxisScale.result.slice())}return t.isBarHorizontal&&t.labels.length&&(t.xTickAmount=t.labels.length),this._handleSingleDataPoint(),this._getMinXDiff(),{minX:t.minX,maxX:t.maxX}}},{key:"setZRange",value:function(){var t=this.w.globals;if(t.isDataXYZ)for(var e=0;e<t.series.length;e++)if(void 0!==t.seriesZ[e])for(var i=0;i<t.seriesZ[e].length;i++)null!==t.seriesZ[e][i]&&f.isNumber(t.seriesZ[e][i])&&(t.maxZ=Math.max(t.maxZ,t.seriesZ[e][i]),t.minZ=Math.min(t.minZ,t.seriesZ[e][i]))}},{key:"_handleSingleDataPoint",value:function(){var t=this.w.globals,e=this.w.config;if(t.minX===t.maxX){var i=new R(this.ctx);if("datetime"===e.xaxis.type){var a=i.getDate(t.minX);a.setUTCDate(a.getDate()-2),t.minX=new Date(a).getTime();var s=i.getDate(t.maxX);s.setUTCDate(s.getDate()+2),t.maxX=new Date(s).getTime()}else("numeric"===e.xaxis.type||"category"===e.xaxis.type&&!t.noLabelsProvided)&&(t.minX=t.minX-2,t.initialMinX=t.minX,t.maxX=t.maxX+2,t.initialMaxX=t.maxX)}}},{key:"_getMinXDiff",value:function(){var t=this.w.globals;t.isXNumeric&&t.seriesX.forEach((function(e,i){1===e.length&&e.push(t.seriesX[t.maxValsInArrayIndex][t.seriesX[t.maxValsInArrayIndex].length-1]);var a=e.slice();a.sort((function(t,e){return t-e})),a.forEach((function(e,a){if(a>0){var s=e-t.seriesX[i][a-1];s>0&&(t.minXDiff=Math.min(s,t.minXDiff))}})),1===t.dataPoints&&t.minXDiff===Number.MAX_VALUE&&(t.minXDiff=.5)}))}},{key:"_setStackedMinMax",value:function(){var t=this.w.globals,e=[],i=[];if(t.series.length)for(var a=0;a<t.series[t.maxValsInArrayIndex].length;a++)for(var s=0,r=0,n=0;n<t.series.length;n++)null!==t.series[n][a]&&f.isNumber(t.series[n][a])&&(t.series[n][a]>0?s=s+parseFloat(t.series[n][a])+1e-4:r+=parseFloat(t.series[n][a])),n===t.series.length-1&&(e.push(s),i.push(r));for(var o=0;o<e.length;o++)t.maxY=Math.max(t.maxY,e[o]),t.minY=Math.min(t.minY,i[o])}}]),t}(),$=function(){function t(i){e(this,t),this.ctx=i,this.w=i.w;var a=this.w;this.xaxisFontSize=a.config.xaxis.labels.style.fontSize,this.axisFontFamily=a.config.xaxis.labels.style.fontFamily,this.xaxisForeColors=a.config.xaxis.labels.style.colors,this.isCategoryBarHorizontal="bar"===a.config.chart.type&&a.config.plotOptions.bar.horizontal,this.xAxisoffX=0,"bottom"===a.config.xaxis.position&&(this.xAxisoffX=a.globals.gridHeight),this.drawnLabels=[],this.axesUtils=new G(i)}return a(t,[{key:"drawYaxis",value:function(t){var e=this.w,i=new b(this.ctx),a=e.config.yaxis[t].labels.style,s=a.fontSize,r=a.fontFamily,n=a.fontWeight,o=i.group({class:"apexcharts-yaxis",rel:t,transform:"translate("+e.globals.translateYAxisX[t]+", 0)"});if(this.axesUtils.isYAxisHidden(t))return o;var l=i.group({class:"apexcharts-yaxis-texts-g"});o.add(l);var h=e.globals.yAxisScale[t].result.length-1,c=e.globals.gridHeight/h,d=e.globals.translateY,g=e.globals.yLabelFormatters[t],u=e.globals.yAxisScale[t].result.slice();u=this.axesUtils.checkForReversedLabels(t,u);var f="";if(e.config.yaxis[t].labels.show)for(var p=function(o){var p=u[o];p=g(p,o);var x=e.config.yaxis[t].labels.padding;e.config.yaxis[t].opposite&&0!==e.config.yaxis.length&&(x*=-1);var b=i.drawText({x:x,y:d+h/10+e.config.yaxis[t].labels.offsetY+1,text:p,textAnchor:e.config.yaxis[t].opposite?"start":"end",fontSize:s,fontFamily:r,fontWeight:n,foreColor:Array.isArray(a.colors)?a.colors[o]:a.colors,isPlainText:!1,cssClass:"apexcharts-yaxis-label "+a.cssClass});if(o===h&&(f=b),l.add(b),0!==e.config.yaxis[t].labels.rotate){var m=i.rotateAroundCenter(f.node),v=i.rotateAroundCenter(b.node);b.node.setAttribute("transform","rotate(".concat(e.config.yaxis[t].labels.rotate," ").concat(m.x," ").concat(v.y,")"))}d+=c},x=h;x>=0;x--)p(x);if(void 0!==e.config.yaxis[t].title.text){var m=i.group({class:"apexcharts-yaxis-title"}),v=0;e.config.yaxis[t].opposite&&(v=e.globals.translateYAxisX[t]);var y=i.drawText({x:v,y:e.globals.gridHeight/2+e.globals.translateY+e.config.yaxis[t].title.offsetY,text:e.config.yaxis[t].title.text,textAnchor:"end",foreColor:e.config.yaxis[t].title.style.color,fontSize:e.config.yaxis[t].title.style.fontSize,fontWeight:e.config.yaxis[t].title.style.fontWeight,fontFamily:e.config.yaxis[t].title.style.fontFamily,cssClass:"apexcharts-yaxis-title-text "+e.config.yaxis[t].title.style.cssClass});m.add(y),o.add(m)}var w=e.config.yaxis[t].axisBorder,k=31+w.offsetX;if(e.config.yaxis[t].opposite&&(k=-31-w.offsetX),w.show){var A=i.drawLine(k,e.globals.translateY+w.offsetY-2,k,e.globals.gridHeight+e.globals.translateY+w.offsetY+2,w.color,0,w.width);o.add(A)}return e.config.yaxis[t].axisTicks.show&&this.axesUtils.drawYAxisTicks(k,h,w,e.config.yaxis[t].axisTicks,t,c,o),o}},{key:"drawYaxisInversed",value:function(t){var e=this.w,i=new b(this.ctx),a=i.group({class:"apexcharts-xaxis apexcharts-yaxis-inversed"}),s=i.group({class:"apexcharts-xaxis-texts-g",transform:"translate(".concat(e.globals.translateXAxisX,", ").concat(e.globals.translateXAxisY,")")});a.add(s);var r=e.globals.yAxisScale[t].result.length-1,n=e.globals.gridWidth/r+.1,o=n+e.config.xaxis.labels.offsetX,l=e.globals.xLabelFormatter,h=e.globals.yAxisScale[t].result.slice(),c=e.globals.timescaleLabels;c.length>0&&(this.xaxisLabels=c.slice(),r=(h=c.slice()).length),h=this.axesUtils.checkForReversedLabels(t,h);var d=c.length;if(e.config.xaxis.labels.show)for(var g=d?0:r;d?g<d:g>=0;d?g++:g--){var u=h[g];u=l(u,g);var f=e.globals.gridWidth+e.globals.padHorizontal-(o-n+e.config.xaxis.labels.offsetX);if(c.length){var p=this.axesUtils.getLabel(h,c,f,g,this.drawnLabels,this.xaxisFontSize);f=p.x,u=p.text,this.drawnLabels.push(p.text),0===g&&e.globals.skipFirstTimelinelabel&&(u=""),g===h.length-1&&e.globals.skipLastTimelinelabel&&(u="")}var x=i.drawText({x:f,y:this.xAxisoffX+e.config.xaxis.labels.offsetY+30-("top"===e.config.xaxis.position?e.globals.xAxisHeight+e.config.xaxis.axisTicks.height-2:0),text:u,textAnchor:"middle",foreColor:Array.isArray(this.xaxisForeColors)?this.xaxisForeColors[t]:this.xaxisForeColors,fontSize:this.xaxisFontSize,fontFamily:this.xaxisFontFamily,fontWeight:e.config.xaxis.labels.style.fontWeight,isPlainText:!1,cssClass:"apexcharts-xaxis-label "+e.config.xaxis.labels.style.cssClass});s.add(x),x.tspan(u);var m=document.createElementNS(e.globals.SVGNS,"title");m.textContent=u,x.node.appendChild(m),o+=n}return this.inversedYAxisTitleText(a),this.inversedYAxisBorder(a),a}},{key:"inversedYAxisBorder",value:function(t){var e=this.w,i=new b(this.ctx),a=e.config.xaxis.axisBorder;if(a.show){var s=0;"bar"===e.config.chart.type&&e.globals.isXNumeric&&(s-=15);var r=i.drawLine(e.globals.padHorizontal+s+a.offsetX,this.xAxisoffX,e.globals.gridWidth,this.xAxisoffX,a.color,0,a.height);t.add(r)}}},{key:"inversedYAxisTitleText",value:function(t){var e=this.w,i=new b(this.ctx);if(void 0!==e.config.xaxis.title.text){var a=i.group({class:"apexcharts-xaxis-title apexcharts-yaxis-title-inversed"}),s=i.drawText({x:e.globals.gridWidth/2+e.config.xaxis.title.offsetX,y:this.xAxisoffX+parseFloat(this.xaxisFontSize)+parseFloat(e.config.xaxis.title.style.fontSize)+e.config.xaxis.title.offsetY+20,text:e.config.xaxis.title.text,textAnchor:"middle",fontSize:e.config.xaxis.title.style.fontSize,fontFamily:e.config.xaxis.title.style.fontFamily,fontWeight:e.config.xaxis.title.style.fontWeight,cssClass:"apexcharts-xaxis-title-text "+e.config.xaxis.title.style.cssClass});a.add(s),t.add(a)}}},{key:"yAxisTitleRotate",value:function(t,e){var i=this.w,a=new b(this.ctx),s={width:0,height:0},r={width:0,height:0},n=i.globals.dom.baseEl.querySelector(" .apexcharts-yaxis[rel='".concat(t,"'] .apexcharts-yaxis-texts-g"));null!==n&&(s=n.getBoundingClientRect());var o=i.globals.dom.baseEl.querySelector(".apexcharts-yaxis[rel='".concat(t,"'] .apexcharts-yaxis-title text"));if(null!==o&&(r=o.getBoundingClientRect()),null!==o){var l=this.xPaddingForYAxisTitle(t,s,r,e);o.setAttribute("x",l.xPos-(e?10:0))}if(null!==o){var h=a.rotateAroundCenter(o);o.setAttribute("transform","rotate(".concat(e?"":"-").concat(i.config.yaxis[t].title.rotate," ").concat(h.x," ").concat(h.y,")"))}}},{key:"xPaddingForYAxisTitle",value:function(t,e,i,a){var s=this.w,r=0,n=0,o=10;return void 0===s.config.yaxis[t].title.text||t<0?{xPos:n,padd:0}:(a?(n=e.width+s.config.yaxis[t].title.offsetX+i.width/2+o/2,0===(r+=1)&&(n-=o/2)):(n=-1*e.width+s.config.yaxis[t].title.offsetX+o/2+i.width/2,s.globals.isBarHorizontal&&(o=25,n=-1*e.width-s.config.yaxis[t].title.offsetX-o)),{xPos:n,padd:o})}},{key:"setYAxisXPosition",value:function(t,e){var i=this.w,a=0,s=0,r=18,n=1;i.config.yaxis.length>1&&(this.multipleYs=!0),i.config.yaxis.map((function(o,l){var h=i.globals.ignoreYAxisIndexes.indexOf(l)>-1||!o.show||o.floating||0===t[l].width,c=t[l].width+e[l].width;o.opposite?i.globals.isBarHorizontal?(s=i.globals.gridWidth+i.globals.translateX-1,i.globals.translateYAxisX[l]=s-o.labels.offsetX):(s=i.globals.gridWidth+i.globals.translateX+n,h||(n=n+c+20),i.globals.translateYAxisX[l]=s-o.labels.offsetX+20):(a=i.globals.translateX-r,h||(r=r+c+20),i.globals.translateYAxisX[l]=a+o.labels.offsetX)}))}},{key:"setYAxisTextAlignments",value:function(){var t=this.w,e=t.globals.dom.baseEl.getElementsByClassName("apexcharts-yaxis");(e=f.listToArray(e)).forEach((function(e,i){var a=t.config.yaxis[i];if(void 0!==a.labels.align){var s=t.globals.dom.baseEl.querySelector(".apexcharts-yaxis[rel='".concat(i,"'] .apexcharts-yaxis-texts-g")),r=t.globals.dom.baseEl.querySelectorAll(".apexcharts-yaxis[rel='".concat(i,"'] .apexcharts-yaxis-label"));r=f.listToArray(r);var n=s.getBoundingClientRect();"left"===a.labels.align?(r.forEach((function(t,e){t.setAttribute("text-anchor","start")})),a.opposite||s.setAttribute("transform","translate(-".concat(n.width,", 0)"))):"center"===a.labels.align?(r.forEach((function(t,e){t.setAttribute("text-anchor","middle")})),s.setAttribute("transform","translate(".concat(n.width/2*(a.opposite?1:-1),", 0)"))):"right"===a.labels.align&&(r.forEach((function(t,e){t.setAttribute("text-anchor","end")})),a.opposite&&s.setAttribute("transform","translate(".concat(n.width,", 0)")))}}))}}]),t}(),J=function(){function t(i){e(this,t),this.ctx=i,this.w=i.w,this.documentEvent=f.bind(this.documentEvent,this)}return a(t,[{key:"addEventListener",value:function(t,e){var i=this.w;i.globals.events.hasOwnProperty(t)?i.globals.events[t].push(e):i.globals.events[t]=[e]}},{key:"removeEventListener",value:function(t,e){var i=this.w;if(i.globals.events.hasOwnProperty(t)){var a=i.globals.events[t].indexOf(e);-1!==a&&i.globals.events[t].splice(a,1)}}},{key:"fireEvent",value:function(t,e){var i=this.w;if(i.globals.events.hasOwnProperty(t)){e&&e.length||(e=[]);for(var a=i.globals.events[t],s=a.length,r=0;r<s;r++)a[r].apply(null,e)}}},{key:"setupEventHandlers",value:function(){var t=this,e=this.w,i=this.ctx,a=e.globals.dom.baseEl.querySelector(e.globals.chartClass);this.ctx.eventList.forEach((function(t){a.addEventListener(t,(function(t){var a=Object.assign({},e,{seriesIndex:e.globals.capturedSeriesIndex,dataPointIndex:e.globals.capturedDataPointIndex});"mousemove"===t.type||"touchmove"===t.type?"function"==typeof e.config.chart.events.mouseMove&&e.config.chart.events.mouseMove(t,i,a):("mouseup"===t.type&&1===t.which||"touchend"===t.type)&&("function"==typeof e.config.chart.events.click&&e.config.chart.events.click(t,i,a),i.ctx.events.fireEvent("click",[t,i,a]))}),{capture:!1,passive:!0})})),this.ctx.eventList.forEach((function(e){document.addEventListener(e,t.documentEvent)})),this.ctx.core.setupBrushHandler()}},{key:"documentEvent",value:function(t){var e=this.w,i=t.target.className;if("click"===t.type){var a=e.globals.dom.baseEl.querySelector(".apexcharts-menu");a&&a.classList.contains("apexcharts-menu-open")&&"apexcharts-menu-icon"!==i&&a.classList.remove("apexcharts-menu-open")}if("mousedown"===t.type){var s=e.globals.dom.Paper.select(".apexcharts-resizable-element").members;Array.prototype.forEach.call(s,(function(e){t.target.classList.contains("apexcharts-resizable-element")||t.target.classList.contains("svg_select_points")||e.selectize(!1)}))}e.globals.clientX="touchmove"===t.type?t.touches[0].clientX:t.clientX,e.globals.clientY="touchmove"===t.type?t.touches[0].clientY:t.clientY}}]),t}(),Q=function(){function t(i){e(this,t),this.ctx=i,this.w=i.w}return a(t,[{key:"setCurrentLocaleValues",value:function(t){var e=this.w.config.chart.locales;window.Apex.chart&&window.Apex.chart.locales&&window.Apex.chart.locales.length>0&&(e=this.w.config.chart.locales.concat(window.Apex.chart.locales));var i=e.filter((function(e){return e.name===t}))[0];if(!i)throw new Error("Wrong locale name provided. Please make sure you set the correct locale name in options");var a=f.extend(C,i);this.w.globals.locale=a.options}}]),t}(),K=function(){function t(i){e(this,t),this.ctx=i,this.w=i.w}return a(t,[{key:"drawAxis",value:function(t,e){var i,a,s=this.w.globals,r=this.w.config,n=new j(this.ctx),o=new $(this.ctx);s.axisCharts&&"radar"!==t&&(s.isBarHorizontal?(a=o.drawYaxisInversed(0),i=n.drawXaxisInversed(0),s.dom.elGraphical.add(i),s.dom.elGraphical.add(a)):(i=n.drawXaxis(),s.dom.elGraphical.add(i),r.yaxis.map((function(t,e){-1===s.ignoreYAxisIndexes.indexOf(e)&&(a=o.drawYaxis(e),s.dom.Paper.add(a))}))));r.yaxis.map((function(t,e){-1===s.ignoreYAxisIndexes.indexOf(e)&&o.yAxisTitleRotate(e,t.opposite)}))}}]),t}(),tt=function(){function t(i){e(this,t),this.ctx=i,this.w=i.w}return a(t,[{key:"drawXCrosshairs",value:function(){var t=this.w,e=new b(this.ctx),i=new p(this.ctx),a=t.config.xaxis.crosshairs.fill.gradient,s=t.config.xaxis.crosshairs.dropShadow,r=t.config.xaxis.crosshairs.fill.type,n=a.colorFrom,o=a.colorTo,l=a.opacityFrom,h=a.opacityTo,c=a.stops,d=s.enabled,g=s.left,u=s.top,x=s.blur,m=s.color,v=s.opacity,y=t.config.xaxis.crosshairs.fill.color;if(t.config.xaxis.crosshairs.show){"gradient"===r&&(y=e.drawGradient("vertical",n,o,l,h,null,c,null));var w=e.drawRect();1===t.config.xaxis.crosshairs.width&&(w=e.drawLine()),w.attr({class:"apexcharts-xcrosshairs",x:0,y:0,y2:t.globals.gridHeight,width:f.isNumber(t.config.xaxis.crosshairs.width)?t.config.xaxis.crosshairs.width:0,height:t.globals.gridHeight,fill:y,filter:"none","fill-opacity":t.config.xaxis.crosshairs.opacity,stroke:t.config.xaxis.crosshairs.stroke.color,"stroke-width":t.config.xaxis.crosshairs.stroke.width,"stroke-dasharray":t.config.xaxis.crosshairs.stroke.dashArray}),d&&(w=i.dropShadow(w,{left:g,top:u,blur:x,color:m,opacity:v})),t.globals.dom.elGraphical.add(w)}}},{key:"drawYCrosshairs",value:function(){var t=this.w,e=new b(this.ctx),i=t.config.yaxis[0].crosshairs,a=t.globals.barPadForNumericAxis;if(t.config.yaxis[0].crosshairs.show){var s=e.drawLine(-a,0,t.globals.gridWidth+a,0,i.stroke.color,i.stroke.dashArray,i.stroke.width);s.attr({class:"apexcharts-ycrosshairs"}),t.globals.dom.elGraphical.add(s)}var r=e.drawLine(-a,0,t.globals.gridWidth+a,0,i.stroke.color,0,0);r.attr({class:"apexcharts-ycrosshairs-hidden"}),t.globals.dom.elGraphical.add(r)}}]),t}(),et=function(){function t(i){e(this,t),this.ctx=i,this.w=i.w}return a(t,[{key:"checkResponsiveConfig",value:function(t){var e=this,i=this.w,a=i.config;if(0!==a.responsive.length){var s=a.responsive.slice();s.sort((function(t,e){return t.breakpoint>e.breakpoint?1:e.breakpoint>t.breakpoint?-1:0})).reverse();var r=new N({}),n=function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},a=s[0].breakpoint,n=window.innerWidth>0?window.innerWidth:screen.width;if(n>a){var o=y.extendArrayProps(r,i.globals.initialConfig,i);t=f.extend(o,t),t=f.extend(i.config,t),e.overrideResponsiveOptions(t)}else for(var l=0;l<s.length;l++)n<s[l].breakpoint&&(t=y.extendArrayProps(r,s[l].options,i),t=f.extend(i.config,t),e.overrideResponsiveOptions(t))};if(t){var o=y.extendArrayProps(r,t,i);o=f.extend(i.config,o),n(o=f.extend(o,t))}else n({})}}},{key:"overrideResponsiveOptions",value:function(t){var e=new N(t).init({responsiveOverride:!0});this.w.config=e}}]),t}(),it=function(){function t(i){e(this,t),this.ctx=i,this.colors=[],this.w=i.w;var a=this.w;this.isColorFn=!1,this.isBarDistributed=a.config.plotOptions.bar.distributed&&("bar"===a.config.chart.type||"rangeBar"===a.config.chart.type)}return a(t,[{key:"init",value:function(){this.setDefaultColors()}},{key:"setDefaultColors",value:function(){var t=this,e=this.w,i=new f;if(e.globals.dom.elWrap.classList.add("apexcharts-theme-".concat(e.config.theme.mode)),void 0===e.config.colors?e.globals.colors=this.predefined():(e.globals.colors=e.config.colors,Array.isArray(e.config.colors)&&e.config.colors.length>0&&"function"==typeof e.config.colors[0]&&(e.globals.colors=e.config.series.map((function(i,a){var s=e.config.colors[a];return s||(s=e.config.colors[0]),"function"==typeof s?(t.isColorFn=!0,s({value:e.globals.axisCharts?e.globals.series[a][0]?e.globals.series[a][0]:0:e.globals.series[a],seriesIndex:a,dataPointIndex:a,w:e})):s})))),e.config.theme.monochrome.enabled){var a=[],s=e.globals.series.length;this.isBarDistributed&&(s=e.globals.series[0].length*e.globals.series.length);for(var r=e.config.theme.monochrome.color,n=1/(s/e.config.theme.monochrome.shadeIntensity),o=e.config.theme.monochrome.shadeTo,l=0,h=0;h<s;h++){var c=void 0;"dark"===o?(c=i.shadeColor(-1*l,r),l+=n):(c=i.shadeColor(l,r),l+=n),a.push(c)}e.globals.colors=a.slice()}var d=e.globals.colors.slice();this.pushExtraColors(e.globals.colors);["fill","stroke"].forEach((function(i){void 0===e.config[i].colors?e.globals[i].colors=t.isColorFn?e.config.colors:d:e.globals[i].colors=e.config[i].colors.slice(),t.pushExtraColors(e.globals[i].colors)})),void 0===e.config.dataLabels.style.colors?e.globals.dataLabels.style.colors=d:e.globals.dataLabels.style.colors=e.config.dataLabels.style.colors.slice(),this.pushExtraColors(e.globals.dataLabels.style.colors,50),void 0===e.config.plotOptions.radar.polygons.fill.colors?e.globals.radarPolygons.fill.colors=["dark"===e.config.theme.mode?"#424242":"#fff"]:e.globals.radarPolygons.fill.colors=e.config.plotOptions.radar.polygons.fill.colors.slice(),this.pushExtraColors(e.globals.radarPolygons.fill.colors,20),void 0===e.config.markers.colors?e.globals.markers.colors=d:e.globals.markers.colors=e.config.markers.colors.slice(),this.pushExtraColors(e.globals.markers.colors)}},{key:"pushExtraColors",value:function(t,e){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null,a=this.w,s=e||a.globals.series.length;if(null===i&&(i=this.isBarDistributed||"heatmap"===a.config.chart.type&&a.config.plotOptions.heatmap.colorScale.inverse),i&&(s=a.globals.series[0].length*a.globals.series.length),t.length<s)for(var r=s-t.length,n=0;n<r;n++)t.push(t[n])}},{key:"updateThemeOptions",value:function(t){t.chart=t.chart||{},t.tooltip=t.tooltip||{};var e=t.theme.mode||"light",i=t.theme.palette?t.theme.palette:"dark"===e?"palette4":"palette1",a=t.chart.foreColor?t.chart.foreColor:"dark"===e?"#f6f7f8":"#373d3f";return t.tooltip.theme=e,t.chart.foreColor=a,t.theme.palette=i,t}},{key:"predefined",value:function(){switch(this.w.config.theme.palette){case"palette1":this.colors=["#008FFB","#00E396","#FEB019","#FF4560","#775DD0"];break;case"palette2":this.colors=["#3f51b5","#03a9f4","#4caf50","#f9ce1d","#FF9800"];break;case"palette3":this.colors=["#33b2df","#546E7A","#d4526e","#13d8aa","#A5978B"];break;case"palette4":this.colors=["#4ecdc4","#c7f464","#81D4FA","#fd6a6a","#546E7A"];break;case"palette5":this.colors=["#2b908f","#f9a3a4","#90ee7e","#fa4443","#69d2e7"];break;case"palette6":this.colors=["#449DD1","#F86624","#EA3546","#662E9B","#C5D86D"];break;case"palette7":this.colors=["#D7263D","#1B998B","#2E294E","#F46036","#E2C044"];break;case"palette8":this.colors=["#662E9B","#F86624","#F9C80E","#EA3546","#43BCCD"];break;case"palette9":this.colors=["#5C4742","#A5978B","#8D5B4C","#5A2A27","#C4BBAF"];break;case"palette10":this.colors=["#A300D6","#7D02EB","#5653FE","#2983FF","#00B1F2"];break;default:this.colors=["#008FFB","#00E396","#FEB019","#FF4560","#775DD0"]}return this.colors}}]),t}(),at=function(){function t(i){e(this,t),this.ctx=i,this.w=i.w}return a(t,[{key:"draw",value:function(){this.drawTitleSubtitle("title"),this.drawTitleSubtitle("subtitle")}},{key:"drawTitleSubtitle",value:function(t){var e=this.w,i="title"===t?e.config.title:e.config.subtitle,a=e.globals.svgWidth/2,s=i.offsetY,r="middle";if("left"===i.align?(a=10,r="start"):"right"===i.align&&(a=e.globals.svgWidth-10,r="end"),a+=i.offsetX,s=s+parseInt(i.style.fontSize,10)+i.margin/2,void 0!==i.text){var n=new b(this.ctx).drawText({x:a,y:s,text:i.text,textAnchor:r,fontSize:i.style.fontSize,fontFamily:i.style.fontFamily,fontWeight:i.style.fontWeight,foreColor:i.style.color,opacity:1});n.node.setAttribute("class","apexcharts-".concat(t,"-text")),e.globals.dom.Paper.add(n)}}}]),t}(),st=function(){function t(i){e(this,t),this.w=i.w,this.dCtx=i}return a(t,[{key:"getTitleSubtitleCoords",value:function(t){var e=this.w,i=0,a=0,s="title"===t?e.config.title.floating:e.config.subtitle.floating,r=e.globals.dom.baseEl.querySelector(".apexcharts-".concat(t,"-text"));if(null!==r&&!s){var n=r.getBoundingClientRect();i=n.width,a=e.globals.axisCharts?n.height+5:n.height}return{width:i,height:a}}},{key:"getLegendsRect",value:function(){var t=this.w,e=t.globals.dom.baseEl.querySelector(".apexcharts-legend"),i=Object.assign({},f.getBoundingClientRect(e));return null!==e&&!t.config.legend.floating&&t.config.legend.show?this.dCtx.lgRect={x:i.x,y:i.y,height:i.height,width:0===i.height?0:i.width}:this.dCtx.lgRect={x:0,y:0,height:0,width:0},"left"!==t.config.legend.position&&"right"!==t.config.legend.position||1.5*this.dCtx.lgRect.width>t.globals.svgWidth&&(this.dCtx.lgRect.width=t.globals.svgWidth/1.5),this.dCtx.lgRect}},{key:"getLargestStringFromMultiArr",value:function(t,e){var i=t;if(this.w.globals.isMultiLineX){var a=e.map((function(t,e){return Array.isArray(t)?t.length:1})),s=Math.max.apply(Math,g(a));i=e[a.indexOf(s)]}return i}}]),t}(),rt=function(){function t(i){e(this,t),this.w=i.w,this.dCtx=i}return a(t,[{key:"getxAxisLabelsCoords",value:function(){var t,e=this.w,i=e.globals.labels.slice();if(e.config.xaxis.convertedCatToNumeric&&0===i.length&&(i=e.globals.categoryLabels),e.globals.timescaleLabels.length>0){var a=this.getxAxisTimeScaleLabelsCoords();t={width:a.width,height:a.height},e.globals.rotateXLabels=!1}else{this.dCtx.lgWidthForSideLegends="left"!==e.config.legend.position&&"right"!==e.config.legend.position||e.config.legend.floating?0:this.dCtx.lgRect.width;var s=e.globals.xLabelFormatter,r=f.getLargestStringFromArr(i),n=this.dCtx.dimHelpers.getLargestStringFromMultiArr(r,i);e.globals.isBarHorizontal&&(n=r=e.globals.yAxisScale[0].result.reduce((function(t,e){return t.length>e.length?t:e}),0));var o=new V(this.dCtx.ctx),l=r;r=o.xLabelFormat(s,r,l),n=o.xLabelFormat(s,n,l),(e.config.xaxis.convertedCatToNumeric&&void 0===r||""===String(r).trim())&&(n=r="1");var h=new b(this.dCtx.ctx),c=h.getTextRects(r,e.config.xaxis.labels.style.fontSize),d=c;if(r!==n&&(d=h.getTextRects(n,e.config.xaxis.labels.style.fontSize)),(t={width:c.width>=d.width?c.width:d.width,height:c.height>=d.height?c.height:d.height}).width*i.length>e.globals.svgWidth-this.dCtx.lgWidthForSideLegends-this.dCtx.yAxisWidth-this.dCtx.gridPad.left-this.dCtx.gridPad.right&&0!==e.config.xaxis.labels.rotate||e.config.xaxis.labels.rotateAlways){if(!e.globals.isBarHorizontal){e.globals.rotateXLabels=!0;var g=function(t){return h.getTextRects(t,e.config.xaxis.labels.style.fontSize,e.config.xaxis.labels.style.fontFamily,"rotate(".concat(e.config.xaxis.labels.rotate," 0 0)"),!1)};c=g(r),r!==n&&(d=g(n)),t.height=(c.height>d.height?c.height:d.height)/1.5,t.width=c.width>d.width?c.width:d.width}}else e.globals.rotateXLabels=!1}return e.config.xaxis.labels.show||(t={width:0,height:0}),{width:t.width,height:t.height}}},{key:"getxAxisTitleCoords",value:function(){var t=this.w,e=0,i=0;if(void 0!==t.config.xaxis.title.text){var a=new b(this.dCtx.ctx).getTextRects(t.config.xaxis.title.text,t.config.xaxis.title.style.fontSize);e=a.width,i=a.height}return{width:e,height:i}}},{key:"getxAxisTimeScaleLabelsCoords",value:function(){var t,e=this.w;this.dCtx.timescaleLabels=e.globals.timescaleLabels.slice();var i=this.dCtx.timescaleLabels.map((function(t){return t.value})),a=i.reduce((function(t,e){return void 0===t?(console.error("You have possibly supplied invalid Date format. Please supply a valid JavaScript Date"),0):t.length>e.length?t:e}),0);return 1.05*(t=new b(this.dCtx.ctx).getTextRects(a,e.config.xaxis.labels.style.fontSize)).width*i.length>e.globals.gridWidth&&0!==e.config.xaxis.labels.rotate&&(e.globals.overlappingXLabels=!0),t}},{key:"additionalPaddingXLabels",value:function(t){var e=this,i=this.w,a=i.globals,s=i.config,r=s.xaxis.type,n=t.width;a.skipLastTimelinelabel=!1,a.skipFirstTimelinelabel=!1;var o=i.config.yaxis[0].opposite&&i.globals.isBarHorizontal,l=function(t,o){(function(t){return-1!==a.collapsedSeriesIndices.indexOf(t)})(o)||("datetime"!==r&&e.dCtx.gridPad.left<n/2-e.dCtx.yAxisWidthLeft&&!a.rotateXLabels&&!s.xaxis.labels.trim&&(e.dCtx.xPadLeft=n/2+1),function(t){if(e.dCtx.timescaleLabels&&e.dCtx.timescaleLabels.length){var s=e.dCtx.timescaleLabels[0],o=e.dCtx.timescaleLabels[e.dCtx.timescaleLabels.length-1].position+n/1.75-e.dCtx.yAxisWidthRight,l=s.position-n/1.75+e.dCtx.yAxisWidthLeft;o>a.gridWidth&&(a.skipLastTimelinelabel=!0),l<0&&(a.skipFirstTimelinelabel=!0)}else"datetime"===r?e.dCtx.gridPad.right<n&&!a.rotateXLabels&&(a.skipLastTimelinelabel=!0):"datetime"!==r&&e.dCtx.gridPad.right<n/2-e.dCtx.yAxisWidthRight&&!a.rotateXLabels&&("between"!==i.config.xaxis.tickPlacement||i.globals.isBarHorizontal)&&(e.dCtx.xPadRight=n/2+1)}())};s.yaxis.forEach((function(t,i){o?(e.dCtx.gridPad.left<n&&(e.dCtx.xPadLeft=n/2+1),e.dCtx.xPadRight=n/2+1):l(0,i)}))}}]),t}(),nt=function(){function t(i){e(this,t),this.w=i.w,this.dCtx=i}return a(t,[{key:"getyAxisLabelsCoords",value:function(){var t=this,e=this.w,i=[],a=10,s=new G(this.dCtx.ctx);return e.config.yaxis.map((function(r,n){var o=e.globals.yAxisScale[n];if(!s.isYAxisHidden(n)&&r.labels.show&&o.result.length){var l=e.globals.yLabelFormatters[n],h=String(o.niceMin).length>String(o.niceMax).length?o.niceMin:o.niceMax,c=l(h,{seriesIndex:n,dataPointIndex:-1,w:e}),d=c;if(void 0!==c&&0!==c.length||(c=h),e.globals.isBarHorizontal){a=0;var g=e.globals.labels.slice();c=l(c=f.getLargestStringFromArr(g),{seriesIndex:n,dataPointIndex:-1,w:e}),d=t.dCtx.dimHelpers.getLargestStringFromMultiArr(c,g)}var u=new b(t.dCtx.ctx),p=u.getTextRects(c,r.labels.style.fontSize),x=p;c!==d&&(x=u.getTextRects(d,r.labels.style.fontSize)),i.push({width:(x.width>p.width?x.width:p.width)+a,height:x.height>p.height?x.height:p.height})}else i.push({width:0,height:0})})),i}},{key:"getyAxisTitleCoords",value:function(){var t=this,e=this.w,i=[];return e.config.yaxis.map((function(e,a){if(e.show&&void 0!==e.title.text){var s=new b(t.dCtx.ctx).getTextRects(e.title.text,e.title.style.fontSize,e.title.style.fontFamily,"rotate(-90 0 0)",!1);i.push({width:s.width,height:s.height})}else i.push({width:0,height:0})})),i}},{key:"getTotalYAxisWidth",value:function(){var t=this.w,e=0,i=0,a=0,s=t.globals.yAxisScale.length>1?10:0,r=new G(this.dCtx.ctx),n=function(n,o){var l=t.config.yaxis[o].floating,h=0;n.width>0&&!l?(h=n.width+s,function(e){return t.globals.ignoreYAxisIndexes.indexOf(e)>-1}(o)&&(h=h-n.width-s)):h=l||r.isYAxisHidden(o)?0:5,t.config.yaxis[o].opposite?a+=h:i+=h,e+=h};return t.globals.yLabelsCoords.map((function(t,e){n(t,e)})),t.globals.yTitleCoords.map((function(t,e){n(t,e)})),t.globals.isBarHorizontal&&(e=t.globals.yLabelsCoords[0].width+t.globals.yTitleCoords[0].width+15),this.dCtx.yAxisWidthLeft=i,this.dCtx.yAxisWidthRight=a,e}}]),t}(),ot=function(){function t(i){e(this,t),this.w=i.w,this.dCtx=i}return a(t,[{key:"gridPadForColumnsInNumericAxis",value:function(t){var e=this.w;if(e.globals.noData||e.globals.allSeriesCollapsed)return 0;var i=e.config.chart.type,a=0,s="bar"===i||"rangeBar"===i?e.config.series.length:1;if(e.globals.comboBarCount>0&&(s=e.globals.comboBarCount),e.globals.collapsedSeries.forEach((function(t){"bar"!==t.type&&"rangeBar"!==t.type||(s-=1)})),e.config.chart.stacked&&(s=1),("bar"===i||"rangeBar"===i||e.globals.comboBarCount>0)&&e.globals.isXNumeric&&!e.globals.isBarHorizontal&&s>0){var r,n,o=Math.abs(e.globals.initialMaxX-e.globals.initialMinX);o<=3&&(o=e.globals.dataPoints),r=o/t,e.globals.minXDiff&&e.globals.minXDiff/r>0&&(n=e.globals.minXDiff/r),n>t/2&&(n/=2),(a=n/s*parseInt(e.config.plotOptions.bar.columnWidth,10)/100)<1&&(a=1),a=a/(s>1?1:1.5)+5,e.globals.barPadForNumericAxis=a}return a}},{key:"gridPadFortitleSubtitle",value:function(){var t=this,e=this.w,i=e.globals,a=this.dCtx.isSparkline||!e.globals.axisCharts?0:10;["title","subtitle"].forEach((function(i){void 0!==e.config[i].text?a+=e.config[i].margin:a+=t.dCtx.isSparkline||!e.globals.axisCharts?0:5}));var s=e.config.series.length>1||!e.globals.axisCharts||e.config.legend.showForSingleSeries;e.config.legend.show&&"bottom"===e.config.legend.position&&!e.config.legend.floating&&s&&(a+=10);var r=this.dCtx.dimHelpers.getTitleSubtitleCoords("title"),n=this.dCtx.dimHelpers.getTitleSubtitleCoords("subtitle");i.gridHeight=i.gridHeight-r.height-n.height-a,i.translateY=i.translateY+r.height+n.height+a}},{key:"setGridXPosForDualYAxis",value:function(t,e){var i=this.w,a=new G(this.dCtx.ctx);i.config.yaxis.map((function(s,r){-1!==i.globals.ignoreYAxisIndexes.indexOf(r)||s.floating||a.isYAxisHidden(r)||(s.opposite&&(i.globals.translateX=i.globals.translateX-(e[r].width+t[r].width)-parseInt(i.config.yaxis[r].labels.style.fontSize,10)/1.2-12),i.globals.translateX<2&&(i.globals.translateX=2))}))}}]),t}(),lt=function(){function t(i){e(this,t),this.ctx=i,this.w=i.w,this.lgRect={},this.yAxisWidth=0,this.yAxisWidthLeft=0,this.yAxisWidthRight=0,this.xAxisHeight=0,this.isSparkline=this.w.config.chart.sparkline.enabled,this.dimHelpers=new st(this),this.dimYAxis=new nt(this),this.dimXAxis=new rt(this),this.dimGrid=new ot(this),this.lgWidthForSideLegends=0,this.gridPad=this.w.config.grid.padding,this.xPadRight=0,this.xPadLeft=0}return a(t,[{key:"plotCoords",value:function(){var t=this.w.globals;this.lgRect=this.dimHelpers.getLegendsRect(),t.axisCharts?this.setDimensionsForAxisCharts():this.setDimensionsForNonAxisCharts(),this.dimGrid.gridPadFortitleSubtitle(),t.gridHeight=t.gridHeight-this.gridPad.top-this.gridPad.bottom,t.gridWidth=t.gridWidth-this.gridPad.left-this.gridPad.right-this.xPadRight-this.xPadLeft;var e=this.dimGrid.gridPadForColumnsInNumericAxis(t.gridWidth);t.gridWidth=t.gridWidth-2*e,t.translateX=t.translateX+this.gridPad.left+this.xPadLeft+(e>0?e+4:0),t.translateY=t.translateY+this.gridPad.top}},{key:"setDimensionsForAxisCharts",value:function(){var t=this,e=this.w,i=e.globals,a=this.dimYAxis.getyAxisLabelsCoords(),s=this.dimYAxis.getyAxisTitleCoords();e.globals.yLabelsCoords=[],e.globals.yTitleCoords=[],e.config.yaxis.map((function(t,i){e.globals.yLabelsCoords.push({width:a[i].width,index:i}),e.globals.yTitleCoords.push({width:s[i].width,index:i})})),this.yAxisWidth=this.dimYAxis.getTotalYAxisWidth();var r=this.dimXAxis.getxAxisLabelsCoords(),n=this.dimXAxis.getxAxisTitleCoords();this.conditionalChecksForAxisCoords(r,n),i.translateXAxisY=e.globals.rotateXLabels?this.xAxisHeight/8:-4,i.translateXAxisX=e.globals.rotateXLabels&&e.globals.isXNumeric&&e.config.xaxis.labels.rotate<=-45?-this.xAxisWidth/4:0,e.globals.isBarHorizontal&&(i.rotateXLabels=!1,i.translateXAxisY=parseInt(e.config.xaxis.labels.style.fontSize,10)/1.5*-1),i.translateXAxisY=i.translateXAxisY+e.config.xaxis.labels.offsetY,i.translateXAxisX=i.translateXAxisX+e.config.xaxis.labels.offsetX;var o=this.yAxisWidth,l=this.xAxisHeight;i.xAxisLabelsHeight=this.xAxisHeight,i.xAxisLabelsWidth=this.xAxisWidth,i.xAxisHeight=this.xAxisHeight;var h=10;("radar"===e.config.chart.type||this.isSparkline)&&(o=0,l=i.goldenPadding),this.isSparkline&&(this.lgRect={height:0,width:0},l=0,o=0,h=0),this.dimXAxis.additionalPaddingXLabels(r);var c=function(){i.translateX=o,i.gridHeight=i.svgHeight-t.lgRect.height-l-(t.isSparkline?0:e.globals.rotateXLabels?10:15),i.gridWidth=i.svgWidth-o};switch("top"===e.config.xaxis.position&&(h=i.xAxisHeight-e.config.xaxis.axisTicks.height-5),e.config.legend.position){case"bottom":i.translateY=h,c();break;case"top":i.translateY=this.lgRect.height+h,c();break;case"left":i.translateY=h,i.translateX=this.lgRect.width+o,i.gridHeight=i.svgHeight-l-12,i.gridWidth=i.svgWidth-this.lgRect.width-o;break;case"right":i.translateY=h,i.translateX=o,i.gridHeight=i.svgHeight-l-12,i.gridWidth=i.svgWidth-this.lgRect.width-o-5;break;default:throw new Error("Legend position not supported")}this.dimGrid.setGridXPosForDualYAxis(s,a),new $(this.ctx).setYAxisXPosition(a,s)}},{key:"setDimensionsForNonAxisCharts",value:function(){var t=this.w,e=t.globals,i=t.config,a=0;t.config.legend.show&&!t.config.legend.floating&&(a=20);var s="pie"===i.chart.type||"polarArea"===i.chart.type||"donut"===i.chart.type?"pie":"radialBar",r=i.plotOptions[s].offsetY,n=i.plotOptions[s].offsetX;if(!i.legend.show||i.legend.floating)return e.gridHeight=e.svgHeight-i.grid.padding.left+i.grid.padding.right,e.gridWidth=e.gridHeight,e.translateY=r,void(e.translateX=n+(e.svgWidth-e.gridWidth)/2);switch(i.legend.position){case"bottom":e.gridHeight=e.svgHeight-this.lgRect.height-e.goldenPadding,e.gridWidth=e.gridHeight,e.translateY=r-10,e.translateX=n+(e.svgWidth-e.gridWidth)/2;break;case"top":e.gridHeight=e.svgHeight-this.lgRect.height-e.goldenPadding,e.gridWidth=e.gridHeight,e.translateY=this.lgRect.height+r+10,e.translateX=n+(e.svgWidth-e.gridWidth)/2;break;case"left":e.gridWidth=e.svgWidth-this.lgRect.width-a,e.gridHeight="auto"!==i.chart.height?e.svgHeight:e.gridWidth,e.translateY=r,e.translateX=n+this.lgRect.width+a;break;case"right":e.gridWidth=e.svgWidth-this.lgRect.width-a-5,e.gridHeight="auto"!==i.chart.height?e.svgHeight:e.gridWidth,e.translateY=r,e.translateX=n+10;break;default:throw new Error("Legend position not supported")}}},{key:"conditionalChecksForAxisCoords",value:function(t,e){var i=this.w;this.xAxisHeight=(t.height+e.height)*(i.globals.isMultiLineX?1.2:i.globals.LINE_HEIGHT_RATIO)+(i.globals.rotateXLabels?22:10),this.xAxisWidth=t.width,this.xAxisHeight-e.height>i.config.xaxis.labels.maxHeight&&(this.xAxisHeight=i.config.xaxis.labels.maxHeight),i.config.xaxis.labels.minHeight&&this.xAxisHeight<i.config.xaxis.labels.minHeight&&(this.xAxisHeight=i.config.xaxis.labels.minHeight),i.config.xaxis.floating&&(this.xAxisHeight=0);var a=0,s=0;i.config.yaxis.forEach((function(t){a+=t.labels.minWidth,s+=t.labels.maxWidth})),this.yAxisWidth<a&&(this.yAxisWidth=a),this.yAxisWidth>s&&(this.yAxisWidth=s)}}]),t}(),ht=function(){function t(i){e(this,t),this.w=i.w,this.lgCtx=i}return a(t,[{key:"getLegendStyles",value:function(){var t=document.createElement("style");t.setAttribute("type","text/css");var e=document.createTextNode("\t\n \t\n .apexcharts-legend {\t\n display: flex;\t\n overflow: auto;\t\n padding: 0 10px;\t\n }\t\n .apexcharts-legend.position-bottom, .apexcharts-legend.position-top {\t\n flex-wrap: wrap\t\n }\t\n .apexcharts-legend.position-right, .apexcharts-legend.position-left {\t\n flex-direction: column;\t\n bottom: 0;\t\n }\t\n .apexcharts-legend.position-bottom.apexcharts-align-left, .apexcharts-legend.position-top.apexcharts-align-left, .apexcharts-legend.position-right, .apexcharts-legend.position-left {\t\n justify-content: flex-start;\t\n }\t\n .apexcharts-legend.position-bottom.apexcharts-align-center, .apexcharts-legend.position-top.apexcharts-align-center {\t\n justify-content: center; \t\n }\t\n .apexcharts-legend.position-bottom.apexcharts-align-right, .apexcharts-legend.position-top.apexcharts-align-right {\t\n justify-content: flex-end;\t\n }\t\n .apexcharts-legend-series {\t\n cursor: pointer;\t\n line-height: normal;\t\n }\t\n .apexcharts-legend.position-bottom .apexcharts-legend-series, .apexcharts-legend.position-top .apexcharts-legend-series{\t\n display: flex;\t\n align-items: center;\t\n }\t\n .apexcharts-legend-text {\t\n position: relative;\t\n font-size: 14px;\t\n }\t\n .apexcharts-legend-text *, .apexcharts-legend-marker * {\t\n pointer-events: none;\t\n }\t\n .apexcharts-legend-marker {\t\n position: relative;\t\n display: inline-block;\t\n cursor: pointer;\t\n margin-right: 3px;\t\n border-style: solid;\n }\t\n \t\n .apexcharts-legend.apexcharts-align-right .apexcharts-legend-series, .apexcharts-legend.apexcharts-align-left .apexcharts-legend-series{\t\n display: inline-block;\t\n }\t\n .apexcharts-legend-series.apexcharts-no-click {\t\n cursor: auto;\t\n }\t\n .apexcharts-legend .apexcharts-hidden-zero-series, .apexcharts-legend .apexcharts-hidden-null-series {\t\n display: none !important;\t\n }\t\n .apexcharts-inactive-legend {\t\n opacity: 0.45;\t\n }");return t.appendChild(e),t}},{key:"getLegendBBox",value:function(){var t=this.w.globals.dom.baseEl.querySelector(".apexcharts-legend").getBoundingClientRect(),e=t.width;return{clwh:t.height,clww:e}}},{key:"appendToForeignObject",value:function(){var t=this.w.globals;t.dom.elLegendForeign=document.createElementNS(t.SVGNS,"foreignObject");var e=t.dom.elLegendForeign;e.setAttribute("x",0),e.setAttribute("y",0),e.setAttribute("width",t.svgWidth),e.setAttribute("height",t.svgHeight),t.dom.elLegendWrap.setAttribute("xmlns","http://www.w3.org/1999/xhtml"),e.appendChild(t.dom.elLegendWrap),e.appendChild(this.getLegendStyles()),t.dom.Paper.node.insertBefore(e,t.dom.elGraphical.node)}},{key:"toggleDataSeries",value:function(t,e){var i=this,a=this.w;if(a.globals.axisCharts||"radialBar"===a.config.chart.type){a.globals.resized=!0;var s=null,r=null;if(a.globals.risingSeries=[],a.globals.axisCharts?(s=a.globals.dom.baseEl.querySelector(".apexcharts-series[data\\:realIndex='".concat(t,"']")),r=parseInt(s.getAttribute("data:realIndex"),10)):(s=a.globals.dom.baseEl.querySelector(".apexcharts-series[rel='".concat(t+1,"']")),r=parseInt(s.getAttribute("rel"),10)-1),e)[{cs:a.globals.collapsedSeries,csi:a.globals.collapsedSeriesIndices},{cs:a.globals.ancillaryCollapsedSeries,csi:a.globals.ancillaryCollapsedSeriesIndices}].forEach((function(t){i.riseCollapsedSeries(t.cs,t.csi,r)}));else this.hideSeries({seriesEl:s,realIndex:r})}else{var n=a.globals.dom.Paper.select(" .apexcharts-series[rel='".concat(t+1,"'] path")),o=a.config.chart.type;if("pie"===o||"polarArea"===o||"donut"===o){var l=a.config.plotOptions.pie.donut.labels;new b(this.lgCtx.ctx).pathMouseDown(n.members[0],null),this.lgCtx.ctx.pie.printDataLabelsInner(n.members[0].node,l)}n.fire("click")}}},{key:"hideSeries",value:function(t){var e=t.seriesEl,i=t.realIndex,a=this.w,s=f.clone(a.config.series);if(a.globals.axisCharts){var r=!1;if(a.config.yaxis[i]&&a.config.yaxis[i].show&&a.config.yaxis[i].showAlways&&(r=!0,a.globals.ancillaryCollapsedSeriesIndices.indexOf(i)<0&&(a.globals.ancillaryCollapsedSeries.push({index:i,data:s[i].data.slice(),type:e.parentNode.className.baseVal.split("-")[1]}),a.globals.ancillaryCollapsedSeriesIndices.push(i))),!r){a.globals.collapsedSeries.push({index:i,data:s[i].data.slice(),type:e.parentNode.className.baseVal.split("-")[1]}),a.globals.collapsedSeriesIndices.push(i);var n=a.globals.risingSeries.indexOf(i);a.globals.risingSeries.splice(n,1)}}else a.globals.collapsedSeries.push({index:i,data:s[i]}),a.globals.collapsedSeriesIndices.push(i);for(var o=e.childNodes,l=0;l<o.length;l++)o[l].classList.contains("apexcharts-series-markers-wrap")&&(o[l].classList.contains("apexcharts-hide")?o[l].classList.remove("apexcharts-hide"):o[l].classList.add("apexcharts-hide"));a.globals.allSeriesCollapsed=a.globals.collapsedSeries.length===a.config.series.length,s=this._getSeriesBasedOnCollapsedState(s),this.lgCtx.ctx.updateHelpers._updateSeries(s,a.config.chart.animations.dynamicAnimation.enabled)}},{key:"riseCollapsedSeries",value:function(t,e,i){var a=this.w,s=f.clone(a.config.series);if(t.length>0){for(var r=0;r<t.length;r++)t[r].index===i&&(a.globals.axisCharts?(s[i].data=t[r].data.slice(),t.splice(r,1),e.splice(r,1),a.globals.risingSeries.push(i)):(s[i]=t[r].data,t.splice(r,1),e.splice(r,1),a.globals.risingSeries.push(i)));s=this._getSeriesBasedOnCollapsedState(s),this.lgCtx.ctx.updateHelpers._updateSeries(s,a.config.chart.animations.dynamicAnimation.enabled)}}},{key:"_getSeriesBasedOnCollapsedState",value:function(t){var e=this.w;return e.globals.axisCharts?t.forEach((function(i,a){e.globals.collapsedSeriesIndices.indexOf(a)>-1&&(t[a].data=[])})):t.forEach((function(i,a){e.globals.collapsedSeriesIndices.indexOf(a)>-1&&(t[a]=0)})),t}}]),t}(),ct=function(){function t(i,a){e(this,t),this.ctx=i,this.w=i.w,this.onLegendClick=this.onLegendClick.bind(this),this.onLegendHovered=this.onLegendHovered.bind(this),this.isBarsDistributed="bar"===this.w.config.chart.type&&this.w.config.plotOptions.bar.distributed&&1===this.w.config.series.length,this.legendHelpers=new ht(this)}return a(t,[{key:"init",value:function(){var t=this.w,e=t.globals,i=t.config;if((i.legend.showForSingleSeries&&1===e.series.length||this.isBarsDistributed||e.series.length>1||!e.axisCharts)&&i.legend.show){for(;e.dom.elLegendWrap.firstChild;)e.dom.elLegendWrap.removeChild(e.dom.elLegendWrap.firstChild);this.drawLegends(),f.isIE11()?document.getElementsByTagName("head")[0].appendChild(this.legendHelpers.getLegendStyles()):this.legendHelpers.appendToForeignObject(),"bottom"===i.legend.position||"top"===i.legend.position?this.legendAlignHorizontal():"right"!==i.legend.position&&"left"!==i.legend.position||this.legendAlignVertical()}}},{key:"drawLegends",value:function(){var t=this.w,e=t.config.legend.fontFamily,i=t.globals.seriesNames,a=t.globals.colors.slice();if("heatmap"===t.config.chart.type){var s=t.config.plotOptions.heatmap.colorScale.ranges;i=s.map((function(t){return t.name?t.name:t.from+" - "+t.to})),a=s.map((function(t){return t.color}))}else this.isBarsDistributed&&(i=t.globals.labels.slice());for(var r=t.globals.legendFormatter,n=t.config.legend.inverseOrder,o=n?i.length-1:0;n?o>=0:o<=i.length-1;n?o--:o++){var l=r(i[o],{seriesIndex:o,w:t}),h=!1,c=!1;if(t.globals.collapsedSeries.length>0)for(var d=0;d<t.globals.collapsedSeries.length;d++)t.globals.collapsedSeries[d].index===o&&(h=!0);if(t.globals.ancillaryCollapsedSeriesIndices.length>0)for(var g=0;g<t.globals.ancillaryCollapsedSeriesIndices.length;g++)t.globals.ancillaryCollapsedSeriesIndices[g]===o&&(c=!0);var u=document.createElement("span");u.classList.add("apexcharts-legend-marker");var f=t.config.legend.markers.offsetX,p=t.config.legend.markers.offsetY,x=t.config.legend.markers.height,m=t.config.legend.markers.width,v=t.config.legend.markers.strokeWidth,w=t.config.legend.markers.strokeColor,k=t.config.legend.markers.radius,A=u.style;A.background=a[o],A.color=a[o],t.config.legend.markers.fillColors&&t.config.legend.markers.fillColors[o]&&(A.background=t.config.legend.markers.fillColors[o]),A.height=Array.isArray(x)?parseFloat(x[o])+"px":parseFloat(x)+"px",A.width=Array.isArray(m)?parseFloat(m[o])+"px":parseFloat(m)+"px",A.left=Array.isArray(f)?f[o]:f,A.top=Array.isArray(p)?p[o]:p,A.borderWidth=Array.isArray(v)?v[o]:v,A.borderColor=Array.isArray(w)?w[o]:w,A.borderRadius=Array.isArray(k)?parseFloat(k[o])+"px":parseFloat(k)+"px",t.config.legend.markers.customHTML&&(Array.isArray(t.config.legend.markers.customHTML)?t.config.legend.markers.customHTML[o]&&(u.innerHTML=t.config.legend.markers.customHTML[o]()):u.innerHTML=t.config.legend.markers.customHTML()),b.setAttrs(u,{rel:o+1,"data:collapsed":h||c}),(h||c)&&u.classList.add("apexcharts-inactive-legend");var S=document.createElement("div"),C=document.createElement("span");C.classList.add("apexcharts-legend-text"),C.innerHTML=Array.isArray(l)?l.join(" "):l;var L=t.config.legend.labels.useSeriesColors?t.globals.colors[o]:t.config.legend.labels.colors;L||(L=t.config.chart.foreColor),C.style.color=L,C.style.fontSize=parseFloat(t.config.legend.fontSize)+"px",C.style.fontWeight=t.config.legend.fontWeight,C.style.fontFamily=e||t.config.chart.fontFamily,b.setAttrs(C,{rel:o+1,i:o,"data:default-text":encodeURIComponent(l),"data:collapsed":h||c}),S.appendChild(u),S.appendChild(C);var P=new y(this.ctx);if(!t.config.legend.showForZeroSeries)0===P.getSeriesTotalByIndex(o)&&P.seriesHaveSameValues(o)&&!P.isSeriesNull(o)&&-1===t.globals.collapsedSeriesIndices.indexOf(o)&&-1===t.globals.ancillaryCollapsedSeriesIndices.indexOf(o)&&S.classList.add("apexcharts-hidden-zero-series");t.config.legend.showForNullSeries||P.isSeriesNull(o)&&-1===t.globals.collapsedSeriesIndices.indexOf(o)&&-1===t.globals.ancillaryCollapsedSeriesIndices.indexOf(o)&&S.classList.add("apexcharts-hidden-null-series"),t.globals.dom.elLegendWrap.appendChild(S),t.globals.dom.elLegendWrap.classList.add("apexcharts-align-".concat(t.config.legend.horizontalAlign)),t.globals.dom.elLegendWrap.classList.add("position-"+t.config.legend.position),S.classList.add("apexcharts-legend-series"),S.style.margin="".concat(t.config.legend.itemMargin.vertical,"px ").concat(t.config.legend.itemMargin.horizontal,"px"),t.globals.dom.elLegendWrap.style.width=t.config.legend.width?t.config.legend.width+"px":"",t.globals.dom.elLegendWrap.style.height=t.config.legend.height?t.config.legend.height+"px":"",b.setAttrs(S,{rel:o+1,"data:collapsed":h||c}),(h||c)&&S.classList.add("apexcharts-inactive-legend"),t.config.legend.onItemClick.toggleDataSeries||S.classList.add("apexcharts-no-click")}"heatmap"!==t.config.chart.type&&!this.isBarsDistributed&&t.config.legend.onItemClick.toggleDataSeries&&t.globals.dom.elWrap.addEventListener("click",this.onLegendClick,!0),t.config.legend.onItemHover.highlightDataSeries&&(t.globals.dom.elWrap.addEventListener("mousemove",this.onLegendHovered,!0),t.globals.dom.elWrap.addEventListener("mouseout",this.onLegendHovered,!0))}},{key:"setLegendWrapXY",value:function(t,e){var i=this.w,a=i.globals.dom.baseEl.querySelector(".apexcharts-legend"),s=a.getBoundingClientRect(),r=0,n=0;if("bottom"===i.config.legend.position)n+=i.globals.svgHeight-s.height/2;else if("top"===i.config.legend.position){var o=new lt(this.ctx),l=o.dimHelpers.getTitleSubtitleCoords("title").height,h=o.dimHelpers.getTitleSubtitleCoords("subtitle").height;n=n+(l>0?l-10:0)+(h>0?h-10:0)}a.style.position="absolute",r=r+t+i.config.legend.offsetX,n=n+e+i.config.legend.offsetY,a.style.left=r+"px",a.style.top=n+"px","bottom"===i.config.legend.position?(a.style.top="auto",a.style.bottom=5-i.config.legend.offsetY+"px"):"right"===i.config.legend.position&&(a.style.left="auto",a.style.right=25+i.config.legend.offsetX+"px");["width","height"].forEach((function(t){a.style[t]&&(a.style[t]=parseInt(i.config.legend[t],10)+"px")}))}},{key:"legendAlignHorizontal",value:function(){var t=this.w;t.globals.dom.baseEl.querySelector(".apexcharts-legend").style.right=0;var e=this.legendHelpers.getLegendBBox(),i=new lt(this.ctx),a=i.dimHelpers.getTitleSubtitleCoords("title"),s=i.dimHelpers.getTitleSubtitleCoords("subtitle"),r=0;"bottom"===t.config.legend.position?r=-e.clwh/1.8:"top"===t.config.legend.position&&(r=a.height+s.height+t.config.title.margin+t.config.subtitle.margin-10),this.setLegendWrapXY(20,r)}},{key:"legendAlignVertical",value:function(){var t=this.w,e=this.legendHelpers.getLegendBBox(),i=0;"left"===t.config.legend.position&&(i=20),"right"===t.config.legend.position&&(i=t.globals.svgWidth-e.clww-10),this.setLegendWrapXY(i,20)}},{key:"onLegendHovered",value:function(t){var e=this.w,i=t.target.classList.contains("apexcharts-legend-text")||t.target.classList.contains("apexcharts-legend-marker");if("heatmap"===e.config.chart.type||this.isBarsDistributed){if(i){var a=parseInt(t.target.getAttribute("rel"),10)-1;this.ctx.events.fireEvent("legendHover",[this.ctx,a,this.w]),new X(this.ctx).highlightRangeInSeries(t,t.target)}}else!t.target.classList.contains("apexcharts-inactive-legend")&&i&&new X(this.ctx).toggleSeriesOnHover(t,t.target)}},{key:"onLegendClick",value:function(t){if(t.target.classList.contains("apexcharts-legend-text")||t.target.classList.contains("apexcharts-legend-marker")){var e=parseInt(t.target.getAttribute("rel"),10)-1,i="true"===t.target.getAttribute("data:collapsed"),a=this.w.config.chart.events.legendClick;"function"==typeof a&&a(this.ctx,e,this.w),this.ctx.events.fireEvent("legendClick",[this.ctx,e,this.w]);var s=this.w.config.legend.markers.onClick;"function"==typeof s&&t.target.classList.contains("apexcharts-legend-marker")&&(s(this.ctx,e,this.w),this.ctx.events.fireEvent("legendMarkerClick",[this.ctx,e,this.w])),this.legendHelpers.toggleDataSeries(e,i)}}}]),t}(),dt=function(){function t(i){e(this,t),this.ctx=i,this.w=i.w,this.ev=this.w.config.chart.events,this.selectedClass="apexcharts-selected",this.localeValues=this.w.globals.locale.toolbar}return a(t,[{key:"createToolbar",value:function(){var t=this,e=this.w,i=function(){return document.createElement("div")},a=i();if(a.setAttribute("class","apexcharts-toolbar"),a.style.top=e.config.chart.toolbar.offsetY+"px",a.style.right=3-e.config.chart.toolbar.offsetX+"px",e.globals.dom.elWrap.appendChild(a),this.elZoom=i(),this.elZoomIn=i(),this.elZoomOut=i(),this.elPan=i(),this.elSelection=i(),this.elZoomReset=i(),this.elMenuIcon=i(),this.elMenu=i(),this.elCustomIcons=[],this.t=e.config.chart.toolbar.tools,Array.isArray(this.t.customIcons))for(var s=0;s<this.t.customIcons.length;s++)this.elCustomIcons.push(i());var r=[],n=function(i,a,s){var n=i.toLowerCase();t.t[n]&&e.config.chart.zoom.enabled&&r.push({el:a,icon:"string"==typeof t.t[n]?t.t[n]:s,title:t.localeValues[i],class:"apexcharts-".concat(n,"-icon")})};n("zoomIn",this.elZoomIn,'<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">\n <path d="M0 0h24v24H0z" fill="none"/>\n <path d="M13 7h-2v4H7v2h4v4h2v-4h4v-2h-4V7zm-1-5C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"/>\n</svg>\n'),n("zoomOut",this.elZoomOut,'<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">\n <path d="M0 0h24v24H0z" fill="none"/>\n <path d="M7 11v2h10v-2H7zm5-9C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"/>\n</svg>\n');var o=function(i){t.t[i]&&e.config.chart[i].enabled&&r.push({el:"zoom"===i?t.elZoom:t.elSelection,icon:"string"==typeof t.t[i]?t.t[i]:"zoom"===i?'<svg xmlns="http://www.w3.org/2000/svg" fill="#000000" height="24" viewBox="0 0 24 24" width="24">\n <path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/>\n <path d="M0 0h24v24H0V0z" fill="none"/>\n <path d="M12 10h-2v2H9v-2H7V9h2V7h1v2h2v1z"/>\n</svg>':'<svg fill="#6E8192" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">\n <path d="M0 0h24v24H0z" fill="none"/>\n <path d="M3 5h2V3c-1.1 0-2 .9-2 2zm0 8h2v-2H3v2zm4 8h2v-2H7v2zM3 9h2V7H3v2zm10-6h-2v2h2V3zm6 0v2h2c0-1.1-.9-2-2-2zM5 21v-2H3c0 1.1.9 2 2 2zm-2-4h2v-2H3v2zM9 3H7v2h2V3zm2 18h2v-2h-2v2zm8-8h2v-2h-2v2zm0 8c1.1 0 2-.9 2-2h-2v2zm0-12h2V7h-2v2zm0 8h2v-2h-2v2zm-4 4h2v-2h-2v2zm0-16h2V3h-2v2z"/>\n</svg>',title:t.localeValues["zoom"===i?"selectionZoom":"selection"],class:e.globals.isTouchDevice?"apexcharts-element-hidden":"apexcharts-".concat(i,"-icon")})};o("zoom"),o("selection"),this.t.pan&&e.config.chart.zoom.enabled&&r.push({el:this.elPan,icon:"string"==typeof this.t.pan?this.t.pan:'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="#000000" height="24" viewBox="0 0 24 24" width="24">\n <defs>\n <path d="M0 0h24v24H0z" id="a"/>\n </defs>\n <clipPath id="b">\n <use overflow="visible" xlink:href="#a"/>\n </clipPath>\n <path clip-path="url(#b)" d="M23 5.5V20c0 2.2-1.8 4-4 4h-7.3c-1.08 0-2.1-.43-2.85-1.19L1 14.83s1.26-1.23 1.3-1.25c.22-.19.49-.29.79-.29.22 0 .42.06.6.16.04.01 4.31 2.46 4.31 2.46V4c0-.83.67-1.5 1.5-1.5S11 3.17 11 4v7h1V1.5c0-.83.67-1.5 1.5-1.5S15 .67 15 1.5V11h1V2.5c0-.83.67-1.5 1.5-1.5s1.5.67 1.5 1.5V11h1V5.5c0-.83.67-1.5 1.5-1.5s1.5.67 1.5 1.5z"/>\n</svg>',title:this.localeValues.pan,class:e.globals.isTouchDevice?"apexcharts-element-hidden":"apexcharts-pan-icon"}),n("reset",this.elZoomReset,'<svg fill="#000000" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">\n <path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z"/>\n <path d="M0 0h24v24H0z" fill="none"/>\n</svg>'),this.t.download&&r.push({el:this.elMenuIcon,icon:"string"==typeof this.t.download?this.t.download:'<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z"/></svg>',title:this.localeValues.menu,class:"apexcharts-menu-icon"});for(var l=0;l<this.elCustomIcons.length;l++)r.push({el:this.elCustomIcons[l],icon:this.t.customIcons[l].icon,title:this.t.customIcons[l].title,index:this.t.customIcons[l].index,class:"apexcharts-toolbar-custom-icon "+this.t.customIcons[l].class});r.forEach((function(t,e){t.index&&f.moveIndexInArray(r,e,t.index)}));for(var h=0;h<r.length;h++)b.setAttrs(r[h].el,{class:r[h].class,title:r[h].title}),r[h].el.innerHTML=r[h].icon,a.appendChild(r[h].el);this._createHamburgerMenu(a),e.globals.zoomEnabled?this.elZoom.classList.add(this.selectedClass):e.globals.panEnabled?this.elPan.classList.add(this.selectedClass):e.globals.selectionEnabled&&this.elSelection.classList.add(this.selectedClass),this.addToolbarEventListeners()}},{key:"_createHamburgerMenu",value:function(t){this.elMenuItems=[],t.appendChild(this.elMenu),b.setAttrs(this.elMenu,{class:"apexcharts-menu"});var e=[{name:"exportSVG",title:this.localeValues.exportToSVG},{name:"exportPNG",title:this.localeValues.exportToPNG},{name:"exportCSV",title:this.localeValues.exportToCSV}];this.w.globals.allSeriesHasEqualX||e.splice(2,1);for(var i=0;i<e.length;i++)this.elMenuItems.push(document.createElement("div")),this.elMenuItems[i].innerHTML=e[i].title,b.setAttrs(this.elMenuItems[i],{class:"apexcharts-menu-item ".concat(e[i].name),title:e[i].title}),this.elMenu.appendChild(this.elMenuItems[i])}},{key:"addToolbarEventListeners",value:function(){var t=this;this.elZoomReset.addEventListener("click",this.handleZoomReset.bind(this)),this.elSelection.addEventListener("click",this.toggleZoomSelection.bind(this,"selection")),this.elZoom.addEventListener("click",this.toggleZoomSelection.bind(this,"zoom")),this.elZoomIn.addEventListener("click",this.handleZoomIn.bind(this)),this.elZoomOut.addEventListener("click",this.handleZoomOut.bind(this)),this.elPan.addEventListener("click",this.togglePanning.bind(this)),this.elMenuIcon.addEventListener("click",this.toggleMenu.bind(this)),this.elMenuItems.forEach((function(e){e.classList.contains("exportSVG")?e.addEventListener("click",t.handleDownload.bind(t,"svg")):e.classList.contains("exportPNG")?e.addEventListener("click",t.handleDownload.bind(t,"png")):e.classList.contains("exportCSV")&&e.addEventListener("click",t.handleDownload.bind(t,"csv"))}));for(var e=0;e<this.t.customIcons.length;e++)this.elCustomIcons[e].addEventListener("click",this.t.customIcons[e].click.bind(this,this.ctx,this.ctx.w))}},{key:"toggleZoomSelection",value:function(t){this.ctx.getSyncedCharts().forEach((function(e){e.ctx.toolbar.toggleOtherControls();var i="selection"===t?e.ctx.toolbar.elSelection:e.ctx.toolbar.elZoom,a="selection"===t?"selectionEnabled":"zoomEnabled";e.w.globals[a]=!e.w.globals[a],i.classList.contains(e.ctx.toolbar.selectedClass)?i.classList.remove(e.ctx.toolbar.selectedClass):i.classList.add(e.ctx.toolbar.selectedClass)}))}},{key:"getToolbarIconsReference",value:function(){var t=this.w;this.elZoom||(this.elZoom=t.globals.dom.baseEl.querySelector(".apexcharts-zoom-icon")),this.elPan||(this.elPan=t.globals.dom.baseEl.querySelector(".apexcharts-pan-icon")),this.elSelection||(this.elSelection=t.globals.dom.baseEl.querySelector(".apexcharts-selection-icon"))}},{key:"enableZoomPanFromToolbar",value:function(t){this.toggleOtherControls(),"pan"===t?this.w.globals.panEnabled=!0:this.w.globals.zoomEnabled=!0;var e="pan"===t?this.elPan:this.elZoom,i="pan"===t?this.elZoom:this.elPan;e&&e.classList.add(this.selectedClass),i&&i.classList.remove(this.selectedClass)}},{key:"togglePanning",value:function(){this.ctx.getSyncedCharts().forEach((function(t){t.ctx.toolbar.toggleOtherControls(),t.w.globals.panEnabled=!t.w.globals.panEnabled,t.ctx.toolbar.elPan.classList.contains(t.ctx.toolbar.selectedClass)?t.ctx.toolbar.elPan.classList.remove(t.ctx.toolbar.selectedClass):t.ctx.toolbar.elPan.classList.add(t.ctx.toolbar.selectedClass)}))}},{key:"toggleOtherControls",value:function(){var t=this,e=this.w;e.globals.panEnabled=!1,e.globals.zoomEnabled=!1,e.globals.selectionEnabled=!1,this.getToolbarIconsReference(),[this.elPan,this.elSelection,this.elZoom].forEach((function(e){e&&e.classList.remove(t.selectedClass)}))}},{key:"handleZoomIn",value:function(){var t=this.w,e=(t.globals.minX+t.globals.maxX)/2,i=(t.globals.minX+e)/2,a=(t.globals.maxX+e)/2,s=this._getNewMinXMaxX(i,a);t.globals.disableZoomIn||this.zoomUpdateOptions(s.minX,s.maxX)}},{key:"handleZoomOut",value:function(){var t=this.w;if(!("datetime"===t.config.xaxis.type&&new Date(t.globals.minX).getUTCFullYear()<1e3)){var e=(t.globals.minX+t.globals.maxX)/2,i=t.globals.minX-(e-t.globals.minX),a=t.globals.maxX-(e-t.globals.maxX),s=this._getNewMinXMaxX(i,a);t.globals.disableZoomOut||this.zoomUpdateOptions(s.minX,s.maxX)}}},{key:"_getNewMinXMaxX",value:function(t,e){var i=this.w.config.xaxis.convertedCatToNumeric;return{minX:i?Math.floor(t):t,maxX:i?Math.floor(e):e}}},{key:"zoomUpdateOptions",value:function(t,e){var i=this.w;if(void 0!==t||void 0!==e){if(!(i.config.xaxis.convertedCatToNumeric&&(t<1&&(t=1,e=i.globals.dataPoints),e-t<2))){var a={min:t,max:e},s=this.getBeforeZoomRange(a);s&&(a=s.xaxis);var r={xaxis:a},n=f.clone(i.globals.initialConfig.yaxis);if(i.config.chart.zoom.autoScaleYaxis)n=new q(this.ctx).autoScaleY(this.ctx,n,{xaxis:a});i.config.chart.group||(r.yaxis=n),this.w.globals.zoomed=!0,this.ctx.updateHelpers._updateOptions(r,!1,this.w.config.chart.animations.dynamicAnimation.enabled),this.zoomCallback(a,n)}}else this.handleZoomReset()}},{key:"zoomCallback",value:function(t,e){"function"==typeof this.ev.zoomed&&this.ev.zoomed(this.ctx,{xaxis:t,yaxis:e})}},{key:"getBeforeZoomRange",value:function(t,e){var i=null;return"function"==typeof this.ev.beforeZoom&&(i=this.ev.beforeZoom(this,{xaxis:t,yaxis:e})),i}},{key:"toggleMenu",value:function(){var t=this;window.setTimeout((function(){t.elMenu.classList.contains("apexcharts-menu-open")?t.elMenu.classList.remove("apexcharts-menu-open"):t.elMenu.classList.add("apexcharts-menu-open")}),0)}},{key:"handleDownload",value:function(t){var e=this.w,i=new _(this.ctx);switch(t){case"svg":i.exportToSVG(this.ctx);break;case"png":i.exportToPng(this.ctx);break;case"csv":i.exportToCSV({series:e.config.series,columnDelimiter:e.config.chart.toolbar.export.csv.columnDelimiter})}}},{key:"handleZoomReset",value:function(t){this.ctx.getSyncedCharts().forEach((function(t){var e=t.w;e.globals.lastXAxis.min=void 0,e.globals.lastXAxis.max=void 0,t.updateHelpers.revertDefaultAxisMinMax(),"function"==typeof e.config.chart.events.zoomed&&t.ctx.toolbar.zoomCallback({min:e.config.xaxis.min,max:e.config.xaxis.max}),e.globals.zoomed=!1;var i=t.ctx.series.emptyCollapsedSeries(f.clone(e.globals.initialSeries));t.updateHelpers._updateSeries(i,e.config.chart.animations.dynamicAnimation.enabled)}))}},{key:"destroy",value:function(){this.elZoom=null,this.elZoomIn=null,this.elZoomOut=null,this.elPan=null,this.elSelection=null,this.elZoomReset=null,this.elMenuIcon=null}}]),t}(),gt=function(t){o(s,t);var i=d(s);function s(t){var a;return e(this,s),(a=i.call(this,t)).ctx=t,a.w=t.w,a.dragged=!1,a.graphics=new b(a.ctx),a.eventList=["mousedown","mouseleave","mousemove","touchstart","touchmove","mouseup","touchend"],a.clientX=0,a.clientY=0,a.startX=0,a.endX=0,a.dragX=0,a.startY=0,a.endY=0,a.dragY=0,a.moveDirection="none",a}return a(s,[{key:"init",value:function(t){var e=this,i=t.xyRatios,a=this.w,s=this;this.xyRatios=i,this.zoomRect=this.graphics.drawRect(0,0,0,0),this.selectionRect=this.graphics.drawRect(0,0,0,0),this.gridRect=a.globals.dom.baseEl.querySelector(".apexcharts-grid"),this.zoomRect.node.classList.add("apexcharts-zoom-rect"),this.selectionRect.node.classList.add("apexcharts-selection-rect"),a.globals.dom.elGraphical.add(this.zoomRect),a.globals.dom.elGraphical.add(this.selectionRect),"x"===a.config.chart.selection.type?this.slDraggableRect=this.selectionRect.draggable({minX:0,minY:0,maxX:a.globals.gridWidth,maxY:a.globals.gridHeight}).on("dragmove",this.selectionDragging.bind(this,"dragging")):"y"===a.config.chart.selection.type?this.slDraggableRect=this.selectionRect.draggable({minX:0,maxX:a.globals.gridWidth}).on("dragmove",this.selectionDragging.bind(this,"dragging")):this.slDraggableRect=this.selectionRect.draggable().on("dragmove",this.selectionDragging.bind(this,"dragging")),this.preselectedSelection(),this.hoverArea=a.globals.dom.baseEl.querySelector(a.globals.chartClass),this.hoverArea.classList.add("apexcharts-zoomable"),this.eventList.forEach((function(t){e.hoverArea.addEventListener(t,s.svgMouseEvents.bind(s,i),{capture:!1,passive:!0})}))}},{key:"destroy",value:function(){this.slDraggableRect&&(this.slDraggableRect.draggable(!1),this.slDraggableRect.off(),this.selectionRect.off()),this.selectionRect=null,this.zoomRect=null,this.gridRect=null}},{key:"svgMouseEvents",value:function(t,e){var i=this.w,a=this,s=this.ctx.toolbar,r=i.globals.zoomEnabled?i.config.chart.zoom.type:i.config.chart.selection.type,n=i.config.chart.toolbar.autoSelected;if(e.shiftKey?(this.shiftWasPressed=!0,s.enableZoomPanFromToolbar("pan"===n?"zoom":"pan")):this.shiftWasPressed&&(s.enableZoomPanFromToolbar(n),this.shiftWasPressed=!1),!(e.target.classList.contains("apexcharts-selection-rect")||e.target.parentNode.classList.contains("apexcharts-toolbar"))){if(a.clientX="touchmove"===e.type||"touchstart"===e.type?e.touches[0].clientX:"touchend"===e.type?e.changedTouches[0].clientX:e.clientX,a.clientY="touchmove"===e.type||"touchstart"===e.type?e.touches[0].clientY:"touchend"===e.type?e.changedTouches[0].clientY:e.clientY,"mousedown"===e.type&&1===e.which){var o=a.gridRect.getBoundingClientRect();a.startX=a.clientX-o.left,a.startY=a.clientY-o.top,a.dragged=!1,a.w.globals.mousedown=!0}if(("mousemove"===e.type&&1===e.which||"touchmove"===e.type)&&(a.dragged=!0,i.globals.panEnabled?(i.globals.selection=null,a.w.globals.mousedown&&a.panDragging({context:a,zoomtype:r,xyRatios:t})):(a.w.globals.mousedown&&i.globals.zoomEnabled||a.w.globals.mousedown&&i.globals.selectionEnabled)&&(a.selection=a.selectionDrawing({context:a,zoomtype:r}))),"mouseup"===e.type||"touchend"===e.type||"mouseleave"===e.type){var l=a.gridRect.getBoundingClientRect();a.w.globals.mousedown&&(a.endX=a.clientX-l.left,a.endY=a.clientY-l.top,a.dragX=Math.abs(a.endX-a.startX),a.dragY=Math.abs(a.endY-a.startY),(i.globals.zoomEnabled||i.globals.selectionEnabled)&&a.selectionDrawn({context:a,zoomtype:r}),i.globals.panEnabled&&i.config.xaxis.convertedCatToNumeric&&a.delayedPanScrolled()),i.globals.zoomEnabled&&a.hideSelectionRect(this.selectionRect),a.dragged=!1,a.w.globals.mousedown=!1}this.makeSelectionRectDraggable()}}},{key:"makeSelectionRectDraggable",value:function(){var t=this.w;if(this.selectionRect){var e=this.selectionRect.node.getBoundingClientRect();e.width>0&&e.height>0&&this.slDraggableRect.selectize({points:"l, r",pointSize:8,pointType:"rect"}).resize({constraint:{minX:0,minY:0,maxX:t.globals.gridWidth,maxY:t.globals.gridHeight}}).on("resizing",this.selectionDragging.bind(this,"resizing"))}}},{key:"preselectedSelection",value:function(){var t=this.w,e=this.xyRatios;if(!t.globals.zoomEnabled)if(void 0!==t.globals.selection&&null!==t.globals.selection)this.drawSelectionRect(t.globals.selection);else if(void 0!==t.config.chart.selection.xaxis.min&&void 0!==t.config.chart.selection.xaxis.max){var i=(t.config.chart.selection.xaxis.min-t.globals.minX)/e.xRatio,a={x:i,y:0,width:t.globals.gridWidth-(t.globals.maxX-t.config.chart.selection.xaxis.max)/e.xRatio-i,height:t.globals.gridHeight,translateX:0,translateY:0,selectionEnabled:!0};this.drawSelectionRect(a),this.makeSelectionRectDraggable(),"function"==typeof t.config.chart.events.selection&&t.config.chart.events.selection(this.ctx,{xaxis:{min:t.config.chart.selection.xaxis.min,max:t.config.chart.selection.xaxis.max},yaxis:{}})}}},{key:"drawSelectionRect",value:function(t){var e=t.x,i=t.y,a=t.width,s=t.height,r=t.translateX,n=void 0===r?0:r,o=t.translateY,l=void 0===o?0:o,h=this.w,c=this.zoomRect,d=this.selectionRect;if(this.dragged||null!==h.globals.selection){var g={transform:"translate("+n+", "+l+")"};h.globals.zoomEnabled&&this.dragged&&(a<0&&(a=1),c.attr({x:e,y:i,width:a,height:s,fill:h.config.chart.zoom.zoomedArea.fill.color,"fill-opacity":h.config.chart.zoom.zoomedArea.fill.opacity,stroke:h.config.chart.zoom.zoomedArea.stroke.color,"stroke-width":h.config.chart.zoom.zoomedArea.stroke.width,"stroke-opacity":h.config.chart.zoom.zoomedArea.stroke.opacity}),b.setAttrs(c.node,g)),h.globals.selectionEnabled&&(d.attr({x:e,y:i,width:a>0?a:0,height:s>0?s:0,fill:h.config.chart.selection.fill.color,"fill-opacity":h.config.chart.selection.fill.opacity,stroke:h.config.chart.selection.stroke.color,"stroke-width":h.config.chart.selection.stroke.width,"stroke-dasharray":h.config.chart.selection.stroke.dashArray,"stroke-opacity":h.config.chart.selection.stroke.opacity}),b.setAttrs(d.node,g))}}},{key:"hideSelectionRect",value:function(t){t&&t.attr({x:0,y:0,width:0,height:0})}},{key:"selectionDrawing",value:function(t){var e=t.context,i=t.zoomtype,a=this.w,s=e,r=this.gridRect.getBoundingClientRect(),n=s.startX-1,o=s.startY,l=!1,h=!1,c=s.clientX-r.left-n,d=s.clientY-r.top-o,g={};return Math.abs(c+n)>a.globals.gridWidth?c=a.globals.gridWidth-n:s.clientX-r.left<0&&(c=n),n>s.clientX-r.left&&(l=!0,c=Math.abs(c)),o>s.clientY-r.top&&(h=!0,d=Math.abs(d)),g="x"===i?{x:l?n-c:n,y:0,width:c,height:a.globals.gridHeight}:"y"===i?{x:0,y:h?o-d:o,width:a.globals.gridWidth,height:d}:{x:l?n-c:n,y:h?o-d:o,width:c,height:d},s.drawSelectionRect(g),s.selectionDragging("resizing"),g}},{key:"selectionDragging",value:function(t,e){var i=this,a=this.w,s=this.xyRatios,r=this.selectionRect,n=0;"resizing"===t&&(n=30);var o=function(t){return parseFloat(r.node.getAttribute(t))},l={x:o("x"),y:o("y"),width:o("width"),height:o("height")};a.globals.selection=l,"function"==typeof a.config.chart.events.selection&&a.globals.selectionEnabled&&(clearTimeout(this.w.globals.selectionResizeTimer),this.w.globals.selectionResizeTimer=window.setTimeout((function(){var t=i.gridRect.getBoundingClientRect(),e=r.node.getBoundingClientRect(),n=a.globals.xAxisScale.niceMin+(e.left-t.left)*s.xRatio,o=a.globals.xAxisScale.niceMin+(e.right-t.left)*s.xRatio,l=a.globals.yAxisScale[0].niceMin+(t.bottom-e.bottom)*s.yRatio[0],h=a.globals.yAxisScale[0].niceMax-(e.top-t.top)*s.yRatio[0];a.config.chart.events.selection(i.ctx,{xaxis:{min:n,max:o},yaxis:{min:l,max:h}})}),n))}},{key:"selectionDrawn",value:function(t){var e=t.context,i=t.zoomtype,a=this.w,s=e,r=this.xyRatios,n=this.ctx.toolbar;if(s.startX>s.endX){var o=s.startX;s.startX=s.endX,s.endX=o}if(s.startY>s.endY){var l=s.startY;s.startY=s.endY,s.endY=l}var h=a.globals.xAxisScale.niceMin+s.startX*r.xRatio,c=a.globals.xAxisScale.niceMin+s.endX*r.xRatio,d=[],g=[];if(a.config.yaxis.forEach((function(t,e){d.push(a.globals.yAxisScale[e].niceMax-r.yRatio[e]*s.startY),g.push(a.globals.yAxisScale[e].niceMax-r.yRatio[e]*s.endY)})),s.dragged&&(s.dragX>10||s.dragY>10)&&h!==c)if(a.globals.zoomEnabled){var u=f.clone(a.globals.initialConfig.yaxis),p=f.clone(a.globals.initialConfig.xaxis);if(a.globals.zoomed=!0,a.globals.zoomed||(a.globals.lastXAxis=f.clone(a.config.xaxis),a.globals.lastYAxis=f.clone(a.config.yaxis)),a.config.xaxis.convertedCatToNumeric&&(h=Math.floor(h),c=Math.floor(c),h<1&&(h=1,c=a.globals.dataPoints),c-h<2&&(c=h+1)),"xy"!==i&&"x"!==i||(p={min:h,max:c}),"xy"!==i&&"y"!==i||u.forEach((function(t,e){u[e].min=g[e],u[e].max=d[e]})),a.config.chart.zoom.autoScaleYaxis){var x=new q(s.ctx);u=x.autoScaleY(s.ctx,u,{xaxis:p})}if(n){var b=n.getBeforeZoomRange(p,u);b&&(p=b.xaxis?b.xaxis:p,u=b.yaxis?b.yaxe:u)}var m={xaxis:p};a.config.chart.group||(m.yaxis=u),s.ctx.updateHelpers._updateOptions(m,!1,s.w.config.chart.animations.dynamicAnimation.enabled),"function"==typeof a.config.chart.events.zoomed&&n.zoomCallback(p,u)}else if(a.globals.selectionEnabled){var v,y=null;v={min:h,max:c},"xy"!==i&&"y"!==i||(y=f.clone(a.config.yaxis)).forEach((function(t,e){y[e].min=g[e],y[e].max=d[e]})),a.globals.selection=s.selection,"function"==typeof a.config.chart.events.selection&&a.config.chart.events.selection(s.ctx,{xaxis:v,yaxis:y})}}},{key:"panDragging",value:function(t){var e=t.context,i=this.w,a=e;if(void 0!==i.globals.lastClientPosition.x){var s=i.globals.lastClientPosition.x-a.clientX,r=i.globals.lastClientPosition.y-a.clientY;Math.abs(s)>Math.abs(r)&&s>0?this.moveDirection="left":Math.abs(s)>Math.abs(r)&&s<0?this.moveDirection="right":Math.abs(r)>Math.abs(s)&&r>0?this.moveDirection="up":Math.abs(r)>Math.abs(s)&&r<0&&(this.moveDirection="down")}i.globals.lastClientPosition={x:a.clientX,y:a.clientY};var n=i.globals.minX,o=i.globals.maxX;i.config.xaxis.convertedCatToNumeric||a.panScrolled(n,o)}},{key:"delayedPanScrolled",value:function(){var t=this.w,e=t.globals.minX,i=t.globals.maxX,a=(t.globals.maxX-t.globals.minX)/2;"left"===this.moveDirection?(e=t.globals.minX+a,i=t.globals.maxX+a):"right"===this.moveDirection&&(e=t.globals.minX-a,i=t.globals.maxX-a),e=Math.floor(e),i=Math.floor(i),this.updateScrolledChart({xaxis:{min:e,max:i}},e,i)}},{key:"panScrolled",value:function(t,e){var i=this.w,a=this.xyRatios,s=f.clone(i.globals.initialConfig.yaxis);"left"===this.moveDirection?(t=i.globals.minX+i.globals.gridWidth/15*a.xRatio,e=i.globals.maxX+i.globals.gridWidth/15*a.xRatio):"right"===this.moveDirection&&(t=i.globals.minX-i.globals.gridWidth/15*a.xRatio,e=i.globals.maxX-i.globals.gridWidth/15*a.xRatio),(t<i.globals.initialMinX||e>i.globals.initialMaxX)&&(t=i.globals.minX,e=i.globals.maxX);var r={min:t,max:e};i.config.chart.zoom.autoScaleYaxis&&(s=new q(this.ctx).autoScaleY(this.ctx,s,{xaxis:r}));var n={xaxis:{min:t,max:e}};i.config.chart.group||(n.yaxis=s),this.updateScrolledChart(n,t,e)}},{key:"updateScrolledChart",value:function(t,e,i){var a=this.w;this.ctx.updateHelpers._updateOptions(t,!1,!1),"function"==typeof a.config.chart.events.scrolled&&a.config.chart.events.scrolled(this.ctx,{xaxis:{min:e,max:i}})}}]),s}(dt),ut=function(){function t(i){e(this,t),this.w=i.w,this.ttCtx=i,this.ctx=i.ctx}return a(t,[{key:"getNearestValues",value:function(t){var e=t.hoverArea,i=t.elGrid,a=t.clientX,s=t.clientY,r=this.w,n=r.globals.gridWidth,o=n/(r.globals.dataPoints-1),l=i.getBoundingClientRect(),h=this.hasBars();!r.globals.comboCharts&&!h||r.config.xaxis.convertedCatToNumeric||(o=n/r.globals.dataPoints);var c=a-l.left-r.globals.barPadForNumericAxis,d=s-l.top;c<0||d<0||c>r.globals.gridWidth||d>r.globals.gridHeight?(e.classList.remove("hovering-zoom"),e.classList.remove("hovering-pan")):r.globals.zoomEnabled?(e.classList.remove("hovering-pan"),e.classList.add("hovering-zoom")):r.globals.panEnabled&&(e.classList.remove("hovering-zoom"),e.classList.add("hovering-pan"));var g=Math.round(c/o);h&&!r.config.xaxis.convertedCatToNumeric&&(g=Math.ceil(c/o),g-=1);for(var u,p=null,x=null,b=[],m=0;m<r.globals.seriesXvalues.length;m++)b.push([r.globals.seriesXvalues[m][0]-1e-6].concat(r.globals.seriesXvalues[m]));return b=b.map((function(t){return t.filter((function(t){return t}))})),u=r.globals.seriesYvalues.map((function(t){return t.filter((function(t){return f.isNumber(t)}))})),r.globals.isXNumeric&&(p=(x=this.closestInMultiArray(c,d,b,u)).index,g=x.j,null!==p&&(b=r.globals.seriesXvalues[p],g=(x=this.closestInArray(c,b)).index)),r.globals.capturedSeriesIndex=null===p?-1:p,(!g||g<1)&&(g=0),r.globals.capturedDataPointIndex=g,{capturedSeries:p,j:g,hoverX:c,hoverY:d}}},{key:"closestInMultiArray",value:function(t,e,i,a){var s=this.w,r=0,n=null,o=-1;s.globals.series.length>1?r=this.getFirstActiveXArray(i):n=0;var l=a[r][0],h=i[r][0],c=Math.abs(t-h),d=Math.abs(e-l),g=d+c;return a.map((function(s,r){s.map((function(s,l){var h=Math.abs(e-a[r][l]),u=Math.abs(t-i[r][l]),f=u+h;f<g&&(g=f,c=u,d=h,n=r,o=l)}))})),{index:n,j:o}}},{key:"getFirstActiveXArray",value:function(t){for(var e=0,i=new y(this.ctx),a=t.map((function(t,e){return t.length>0?e:-1})),s=0;s<a.length;s++){var r=i.getSeriesTotalByIndex(s);if(-1!==a[s]&&0!==r){e=a[s];break}}return e}},{key:"closestInArray",value:function(t,e){for(var i=e[0],a=null,s=Math.abs(t-i),r=0;r<e.length;r++){var n=Math.abs(t-e[r]);n<s&&(s=n,a=r)}return{index:a}}},{key:"isXoverlap",value:function(t){var e=[],i=this.w.globals.seriesX.filter((function(t){return void 0!==t[0]}));if(i.length>0)for(var a=0;a<i.length-1;a++)void 0!==i[a][t]&&void 0!==i[a+1][t]&&i[a][t]!==i[a+1][t]&&e.push("unEqual");return 0===e.length}},{key:"isInitialSeriesSameLen",value:function(){for(var t=!0,e=this.w.globals.initialSeries,i=0;i<e.length-1;i++)if(e[i].data.length!==e[i+1].data.length){t=!1;break}return t}},{key:"getBarsHeight",value:function(t){return g(t).reduce((function(t,e){return t+e.getBBox().height}),0)}},{key:"getElMarkers",value:function(){return this.w.globals.dom.baseEl.querySelectorAll(" .apexcharts-series-markers")}},{key:"getAllMarkers",value:function(){var t=this.w.globals.dom.baseEl.querySelectorAll(".apexcharts-series-markers-wrap");(t=g(t)).sort((function(t,e){return Number(e.getAttribute("data:realIndex"))<Number(t.getAttribute("data:realIndex"))?0:-1}));var e=[];return t.forEach((function(t){e.push(t.querySelector(".apexcharts-marker"))})),e}},{key:"hasMarkers",value:function(){return this.getElMarkers().length>0}},{key:"getElBars",value:function(){return this.w.globals.dom.baseEl.querySelectorAll(".apexcharts-bar-series, .apexcharts-candlestick-series, .apexcharts-rangebar-series")}},{key:"hasBars",value:function(){return this.getElBars().length>0}},{key:"getHoverMarkerSize",value:function(t){var e=this.w,i=e.config.markers.hover.size;return void 0===i&&(i=e.globals.markers.size[t]+e.config.markers.hover.sizeOffset),i}},{key:"toggleAllTooltipSeriesGroups",value:function(t){var e=this.w,i=this.ttCtx;0===i.allTooltipSeriesGroups.length&&(i.allTooltipSeriesGroups=e.globals.dom.baseEl.querySelectorAll(".apexcharts-tooltip-series-group"));for(var a=i.allTooltipSeriesGroups,s=0;s<a.length;s++)"enable"===t?(a[s].classList.add("apexcharts-active"),a[s].style.display=e.config.tooltip.items.display):(a[s].classList.remove("apexcharts-active"),a[s].style.display="none")}}]),t}(),ft=function(){function t(i){e(this,t),this.w=i.w,this.ctx=i.ctx,this.ttCtx=i,this.tooltipUtil=new ut(i)}return a(t,[{key:"drawSeriesTexts",value:function(t){var e=t.shared,i=void 0===e||e,a=t.ttItems,s=t.i,r=void 0===s?0:s,n=t.j,o=void 0===n?null:n,l=t.y1,h=t.y2,c=this.w;void 0!==c.config.tooltip.custom?this.handleCustomTooltip({i:r,j:o,y1:l,y2:h,w:c}):this.toggleActiveInactiveSeries(i);var d=this.getValuesToPrint({i:r,j:o});this.printLabels({i:r,j:o,values:d,ttItems:a,shared:i});var g=this.ttCtx.getElTooltip();this.ttCtx.tooltipRect.ttWidth=g.getBoundingClientRect().width,this.ttCtx.tooltipRect.ttHeight=g.getBoundingClientRect().height}},{key:"printLabels",value:function(t){var e,i=this,a=t.i,s=t.j,r=t.values,n=t.ttItems,o=t.shared,l=this.w,h=r.xVal,c=r.zVal,d=r.xAxisTTVal,g="",u=l.globals.colors[a];null!==s&&l.config.plotOptions.bar.distributed&&(u=l.globals.colors[s]);for(var f=function(t,r){var f=i.getFormatters(a);g=i.getSeriesName({fn:f.yLbTitleFormatter,index:a,seriesIndex:a,j:s});var p=l.config.tooltip.inverseOrder?r:t;if(l.globals.axisCharts){var x=function(t){return f.yLbFormatter(l.globals.series[t][s],{series:l.globals.series,seriesIndex:t,dataPointIndex:s,w:l})};o?(f=i.getFormatters(p),g=i.getSeriesName({fn:f.yLbTitleFormatter,index:p,seriesIndex:a,j:s}),u=l.globals.colors[p],e=x(p)):e=x(a)}null===s&&(e=f.yLbFormatter(l.globals.series[a],l)),i.DOMHandling({i:a,t:p,j:s,ttItems:n,values:{val:e,xVal:h,xAxisTTVal:d,zVal:c},seriesName:g,shared:o,pColor:u})},p=0,x=l.globals.series.length-1;p<l.globals.series.length;p++,x--)f(p,x)}},{key:"getFormatters",value:function(t){var e,i=this.w,a=i.globals.yLabelFormatters[t];return void 0!==i.globals.ttVal?Array.isArray(i.globals.ttVal)?(a=i.globals.ttVal[t]&&i.globals.ttVal[t].formatter,e=i.globals.ttVal[t]&&i.globals.ttVal[t].title&&i.globals.ttVal[t].title.formatter):(a=i.globals.ttVal.formatter,"function"==typeof i.globals.ttVal.title.formatter&&(e=i.globals.ttVal.title.formatter)):e=i.config.tooltip.y.title.formatter,"function"!=typeof a&&(a=i.globals.yLabelFormatters[0]?i.globals.yLabelFormatters[0]:function(t){return t}),"function"!=typeof e&&(e=function(t){return t}),{yLbFormatter:a,yLbTitleFormatter:e}}},{key:"getSeriesName",value:function(t){var e=t.fn,i=t.index,a=t.seriesIndex,s=t.j,r=this.w;return e(String(r.globals.seriesNames[i]),{series:r.globals.series,seriesIndex:a,dataPointIndex:s,w:r})}},{key:"DOMHandling",value:function(t){t.i;var e=t.t,i=(t.j,t.ttItems),a=t.values,s=t.seriesName,r=t.shared,n=t.pColor,o=this.w,l=this.ttCtx,h=a.val,c=a.xVal,d=a.xAxisTTVal,g=a.zVal,u=null;u=i[e].children,o.config.tooltip.fillSeriesColor&&(i[e].style.backgroundColor=n,u[0].style.display="none"),l.showTooltipTitle&&(null===l.tooltipTitle&&(l.tooltipTitle=o.globals.dom.baseEl.querySelector(".apexcharts-tooltip-title")),l.tooltipTitle.innerHTML=c),l.blxaxisTooltip&&(l.xaxisTooltipText.innerHTML=""!==d?d:c);var f=i[e].querySelector(".apexcharts-tooltip-text-label");f&&(f.innerHTML=s||"");var p=i[e].querySelector(".apexcharts-tooltip-text-value");(p&&(p.innerHTML=void 0!==h?h:""),u[0]&&u[0].classList.contains("apexcharts-tooltip-marker")&&(o.config.tooltip.marker.fillColors&&Array.isArray(o.config.tooltip.marker.fillColors)&&(n=o.config.tooltip.marker.fillColors[e]),u[0].style.backgroundColor=n),o.config.tooltip.marker.show||(u[0].style.display="none"),null!==g)&&(i[e].querySelector(".apexcharts-tooltip-text-z-label").innerHTML=o.config.tooltip.z.title,i[e].querySelector(".apexcharts-tooltip-text-z-value").innerHTML=void 0!==g?g:"");r&&u[0]&&(null==h||o.globals.collapsedSeriesIndices.indexOf(e)>-1?u[0].parentNode.style.display="none":u[0].parentNode.style.display=o.config.tooltip.items.display)}},{key:"toggleActiveInactiveSeries",value:function(t){var e=this.w;if(t)this.tooltipUtil.toggleAllTooltipSeriesGroups("enable");else{this.tooltipUtil.toggleAllTooltipSeriesGroups("disable");var i=e.globals.dom.baseEl.querySelector(".apexcharts-tooltip-series-group");i&&(i.classList.add("apexcharts-active"),i.style.display=e.config.tooltip.items.display)}}},{key:"getValuesToPrint",value:function(t){var e=t.i,i=t.j,a=this.w,s=this.ctx.series.filteredSeriesX(),r="",n="",o=null,l=null,h={series:a.globals.series,seriesIndex:e,dataPointIndex:i,w:a},c=a.globals.ttZFormatter;null===i?l=a.globals.series[e]:a.globals.isXNumeric?(r=s[e][i],0===s[e].length&&(r=s[this.tooltipUtil.getFirstActiveXArray(s)][i])):r=void 0!==a.globals.labels[i]?a.globals.labels[i]:"";var d=r;a.globals.isXNumeric&&"datetime"===a.config.xaxis.type?r=new V(this.ctx).xLabelFormat(a.globals.ttKeyFormatter,d,d):a.globals.isBarHorizontal||(r=a.globals.xLabelFormatter(d,h));return void 0!==a.config.tooltip.x.formatter&&(r=a.globals.ttKeyFormatter(d,h)),a.globals.seriesZ.length>0&&a.globals.seriesZ[0].length>0&&(o=c(a.globals.seriesZ[e][i],a)),n="function"==typeof a.config.xaxis.tooltip.formatter?a.globals.xaxisTooltipFormatter(d,h):r,{val:Array.isArray(l)?l.join(" "):l,xVal:Array.isArray(r)?r.join(" "):r,xAxisTTVal:Array.isArray(n)?n.join(" "):n,zVal:o}}},{key:"handleCustomTooltip",value:function(t){var e=t.i,i=t.j,a=t.y1,s=t.y2,r=t.w,n=this.ttCtx.getElTooltip(),o=r.config.tooltip.custom;Array.isArray(o)&&o[e]&&(o=o[e]),n.innerHTML=o({ctx:this.ctx,series:r.globals.series,seriesIndex:e,dataPointIndex:i,y1:a,y2:s,w:r})}}]),t}(),pt=function(){function t(i){e(this,t),this.ttCtx=i,this.ctx=i.ctx,this.w=i.w}return a(t,[{key:"moveXCrosshairs",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,i=this.ttCtx,a=this.w,s=i.getElXCrosshairs(),r=t-i.xcrosshairsWidth/2,n=a.globals.labels.slice().length;if(null!==e&&(r=a.globals.gridWidth/n*e),null!==s&&(s.setAttribute("x",r),s.setAttribute("x1",r),s.setAttribute("x2",r),s.setAttribute("y2",a.globals.gridHeight),s.classList.add("apexcharts-active")),r<0&&(r=0),r>a.globals.gridWidth&&(r=a.globals.gridWidth),i.blxaxisTooltip){var o=r;"tickWidth"!==a.config.xaxis.crosshairs.width&&"barWidth"!==a.config.xaxis.crosshairs.width||(o=r+i.xcrosshairsWidth/2),this.moveXAxisTooltip(o)}}},{key:"moveYCrosshairs",value:function(t){var e=this.ttCtx;null!==e.ycrosshairs&&b.setAttrs(e.ycrosshairs,{y1:t,y2:t}),null!==e.ycrosshairsHidden&&b.setAttrs(e.ycrosshairsHidden,{y1:t,y2:t})}},{key:"moveXAxisTooltip",value:function(t){var e=this.w,i=this.ttCtx;if(null!==i.xaxisTooltip){i.xaxisTooltip.classList.add("apexcharts-active");var a=i.xaxisOffY+e.config.xaxis.tooltip.offsetY+e.globals.translateY+1+e.config.xaxis.offsetY;if(t-=i.xaxisTooltip.getBoundingClientRect().width/2,!isNaN(t)){t+=e.globals.translateX;var s;s=new b(this.ctx).getTextRects(i.xaxisTooltipText.innerHTML),i.xaxisTooltipText.style.minWidth=s.width+"px",i.xaxisTooltip.style.left=t+"px",i.xaxisTooltip.style.top=a+"px"}}}},{key:"moveYAxisTooltip",value:function(t){var e=this.w,i=this.ttCtx;null===i.yaxisTTEls&&(i.yaxisTTEls=e.globals.dom.baseEl.querySelectorAll(".apexcharts-yaxistooltip"));var a=parseInt(i.ycrosshairsHidden.getAttribute("y1"),10),s=e.globals.translateY+a,r=i.yaxisTTEls[t].getBoundingClientRect().height,n=e.globals.translateYAxisX[t]-2;e.config.yaxis[t].opposite&&(n-=26),s-=r/2,-1===e.globals.ignoreYAxisIndexes.indexOf(t)?(i.yaxisTTEls[t].classList.add("apexcharts-active"),i.yaxisTTEls[t].style.top=s+"px",i.yaxisTTEls[t].style.left=n+e.config.yaxis[t].tooltip.offsetX+"px"):i.yaxisTTEls[t].classList.remove("apexcharts-active")}},{key:"moveTooltip",value:function(t,e){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null,a=this.w,s=this.ttCtx,r=s.getElTooltip(),n=s.tooltipRect,o=null!==i?parseFloat(i):1,l=parseFloat(t)+o+5,h=parseFloat(e)+o/2;if(l>a.globals.gridWidth/2&&(l=l-n.ttWidth-o-15),l>a.globals.gridWidth-n.ttWidth-10&&(l=a.globals.gridWidth-n.ttWidth),l<-20&&(l=-20),a.config.tooltip.followCursor){var c=s.getElGrid(),d=c.getBoundingClientRect();h=s.e.clientY+a.globals.translateY-d.top-n.ttHeight/2}if(!a.config.tooltip.followCursor){var g=this.positionChecks(n,l,h);l=g.x,h=g.y}isNaN(l)||(l+=a.globals.translateX,r.style.left=l+"px",r.style.top=h+"px")}},{key:"positionChecks",value:function(t,e,i){var a=this.w;return t.ttHeight/2+i>a.globals.gridHeight&&(i=a.globals.gridHeight-t.ttHeight+a.globals.translateY),i<0&&(i=0),{x:e,y:i}}},{key:"moveMarkers",value:function(t,e){var i=this.w,a=this.ttCtx;if(i.globals.markers.size[t]>0)for(var s=i.globals.dom.baseEl.querySelectorAll(" .apexcharts-series[data\\:realIndex='".concat(t,"'] .apexcharts-marker")),r=0;r<s.length;r++)parseInt(s[r].getAttribute("rel"),10)===e&&(a.marker.resetPointsSize(),a.marker.enlargeCurrentPoint(e,s[r]));else a.marker.resetPointsSize(),this.moveDynamicPointOnHover(e,t)}},{key:"moveDynamicPointOnHover",value:function(t,e){var i,a,s=this.w,r=this.ttCtx,n=s.globals.pointsArray,o=r.tooltipUtil.getHoverMarkerSize(e),l=s.config.series[e].type;if(!l||"column"!==l&&"candlestick"!==l){i=n[e][t][0],a=n[e][t][1]?n[e][t][1]:0;var h=s.globals.dom.baseEl.querySelector(".apexcharts-series[data\\:realIndex='".concat(e,"'] .apexcharts-series-markers circle"));h&&a<s.globals.gridHeight&&a>0&&(h.setAttribute("r",o),h.setAttribute("cx",i),h.setAttribute("cy",a)),this.moveXCrosshairs(i),r.fixedTooltip||this.moveTooltip(i,a,o)}}},{key:"moveDynamicPointsOnHover",value:function(t){var e,i=this.ttCtx,a=i.w,s=0,r=0,n=a.globals.pointsArray;e=new X(this.ctx).getActiveConfigSeriesIndex(!0);var o=i.tooltipUtil.getHoverMarkerSize(e);n[e]&&(s=n[e][t][0],r=n[e][t][1]);var l=i.tooltipUtil.getAllMarkers();if(null!==l)for(var h=0;h<a.globals.series.length;h++){var c=n[h];if(a.globals.comboCharts&&void 0===c&&l.splice(h,0,null),c&&c.length){var d=n[h][t][1];l[h].setAttribute("cx",s),null!==d&&!isNaN(d)&&d<a.globals.gridHeight&&d>0?(l[h]&&l[h].setAttribute("r",o),l[h]&&l[h].setAttribute("cy",d)):l[h]&&l[h].setAttribute("r",0)}}if(this.moveXCrosshairs(s),!i.fixedTooltip){var g=r||a.globals.gridHeight;this.moveTooltip(s,g,o)}}},{key:"moveStickyTooltipOverBars",value:function(t){var e,i=this.w,a=this.ttCtx,s=i.globals.columnSeries?i.globals.columnSeries.length:i.globals.series.length,r=s>=2&&s%2==0?Math.floor(s/2):Math.floor(s/2)+1,n=i.globals.dom.baseEl.querySelector(".apexcharts-bar-series .apexcharts-series[rel='".concat(r,"'] path[j='").concat(t,"'], .apexcharts-candlestick-series .apexcharts-series[rel='").concat(r,"'] path[j='").concat(t,"'], .apexcharts-rangebar-series .apexcharts-series[rel='").concat(r,"'] path[j='").concat(t,"']")),o=n?parseFloat(n.getAttribute("cx")):0,l=n?parseFloat(n.getAttribute("barWidth")):0;i.globals.isXNumeric?o-=s%2!=0?l/2:0:(o=a.xAxisTicksPositions[t-1]+a.dataPointsDividedWidth/2,isNaN(o)&&(o=a.xAxisTicksPositions[t]-a.dataPointsDividedWidth/2));var h=a.getElGrid().getBoundingClientRect();if(e=a.e.clientY-h.top-a.tooltipRect.ttHeight/2,this.moveXCrosshairs(o),!a.fixedTooltip){var c=e||i.globals.gridHeight;this.moveTooltip(o,c)}}}]),t}(),xt=function(){function t(i){e(this,t),this.w=i.w,this.ttCtx=i,this.ctx=i.ctx,this.tooltipPosition=new pt(i)}return a(t,[{key:"drawDynamicPoints",value:function(){var t=this.w,e=new b(this.ctx),i=new z(this.ctx),a=t.globals.dom.baseEl.querySelectorAll(".apexcharts-series");(a=g(a)).sort((function(t,e){return parseFloat(t.getAttribute("data:realIndex"))-parseFloat(e.getAttribute("data:realIndex"))}));for(var s=0;s<a.length;s++){var r=a[s].querySelector(".apexcharts-series-markers-wrap");if(null!==r){var n=void 0,o="apexcharts-marker w".concat((Math.random()+1).toString(36).substring(4));"line"!==t.config.chart.type&&"area"!==t.config.chart.type||t.globals.comboCharts||t.config.tooltip.intersect||(o+=" no-pointer-events");var l=i.getMarkerConfig(o,s);(n=e.drawMarker(0,0,l)).node.setAttribute("default-marker-size",0);var h=document.createElementNS(t.globals.SVGNS,"g");h.classList.add("apexcharts-series-markers"),h.appendChild(n.node),r.appendChild(h)}}}},{key:"enlargeCurrentPoint",value:function(t,e){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null,a=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null,s=this.w;"bubble"!==s.config.chart.type&&this.newPointSize(t,e);var r=e.getAttribute("cx"),n=e.getAttribute("cy");if(null!==i&&null!==a&&(r=i,n=a),this.tooltipPosition.moveXCrosshairs(r),!this.fixedTooltip){if("radar"===s.config.chart.type){var o=this.ttCtx.getElGrid(),l=o.getBoundingClientRect();r=this.ttCtx.e.clientX-l.left}this.tooltipPosition.moveTooltip(r,n,s.config.markers.hover.size)}}},{key:"enlargePoints",value:function(t){for(var e=this.w,i=this.ttCtx,a=t,s=e.globals.dom.baseEl.querySelectorAll(".apexcharts-series:not(.apexcharts-series-collapsed) .apexcharts-marker"),r=e.config.markers.hover.size,n=0;n<s.length;n++){var o=s[n].getAttribute("rel"),l=s[n].getAttribute("index");if(void 0===r&&(r=e.globals.markers.size[l]+e.config.markers.hover.sizeOffset),a===parseInt(o,10)){this.newPointSize(a,s[n]);var h=s[n].getAttribute("cx"),c=s[n].getAttribute("cy");this.tooltipPosition.moveXCrosshairs(h),i.fixedTooltip||this.tooltipPosition.moveTooltip(h,c,r)}else this.oldPointSize(s[n])}}},{key:"newPointSize",value:function(t,e){var i=this.w,a=i.config.markers.hover.size,s=0===t?e.parentNode.firstChild:e.parentNode.lastChild;if("0"!==s.getAttribute("default-marker-size")){var r=parseInt(s.getAttribute("index"),10);void 0===a&&(a=i.globals.markers.size[r]+i.config.markers.hover.sizeOffset),s.setAttribute("r",a)}}},{key:"oldPointSize",value:function(t){var e=parseFloat(t.getAttribute("default-marker-size"));t.setAttribute("r",e)}},{key:"resetPointsSize",value:function(){for(var t=this.w.globals.dom.baseEl.querySelectorAll(".apexcharts-series:not(.apexcharts-series-collapsed) .apexcharts-marker"),e=0;e<t.length;e++){var i=parseFloat(t[e].getAttribute("default-marker-size"));f.isNumber(i)?t[e].setAttribute("r",i):t[e].setAttribute("r",0)}}}]),t}(),bt=function(){function t(i){e(this,t),this.w=i.w,this.ttCtx=i}return a(t,[{key:"getAttr",value:function(t,e){return parseFloat(t.target.getAttribute(e))}},{key:"handleHeatTooltip",value:function(t){var e=t.e,i=t.opt,a=t.x,s=t.y,r=this.ttCtx,n=this.w;if(e.target.classList.contains("apexcharts-heatmap-rect")){var o=this.getAttr(e,"i"),l=this.getAttr(e,"j"),h=this.getAttr(e,"cx"),c=this.getAttr(e,"cy"),d=this.getAttr(e,"width"),g=this.getAttr(e,"height");if(r.tooltipLabels.drawSeriesTexts({ttItems:i.ttItems,i:o,j:l,shared:!1}),n.globals.capturedSeriesIndex=o,n.globals.capturedDataPointIndex=l,a=h+r.tooltipRect.ttWidth/2+d,s=c+r.tooltipRect.ttHeight/2-g/2,r.tooltipPosition.moveXCrosshairs(h+d/2),a>n.globals.gridWidth/2&&(a=h-r.tooltipRect.ttWidth/2+d),r.w.config.tooltip.followCursor){var u=r.getElGrid().getBoundingClientRect();s=r.e.clientY-u.top+n.globals.translateY/2-10}}return{x:a,y:s}}},{key:"handleMarkerTooltip",value:function(t){var e,i,a=t.e,s=t.opt,r=t.x,n=t.y,o=this.w,l=this.ttCtx;if(a.target.classList.contains("apexcharts-marker")){var h=parseInt(s.paths.getAttribute("cx"),10),c=parseInt(s.paths.getAttribute("cy"),10),d=parseFloat(s.paths.getAttribute("val"));if(i=parseInt(s.paths.getAttribute("rel"),10),e=parseInt(s.paths.parentNode.parentNode.parentNode.getAttribute("rel"),10)-1,l.intersect){var g=f.findAncestor(s.paths,"apexcharts-series");g&&(e=parseInt(g.getAttribute("data:realIndex"),10))}if(l.tooltipLabels.drawSeriesTexts({ttItems:s.ttItems,i:e,j:i,shared:!l.showOnIntersect&&o.config.tooltip.shared}),"mouseup"===a.type&&l.markerClick(a,e,i),o.globals.capturedSeriesIndex=e,o.globals.capturedDataPointIndex=i,r=h,n=c+o.globals.translateY-1.4*l.tooltipRect.ttHeight,l.w.config.tooltip.followCursor){var u=l.getElGrid().getBoundingClientRect();n=l.e.clientY+o.globals.translateY-u.top}d<0&&(n=c),l.marker.enlargeCurrentPoint(i,s.paths,r,n)}return{x:r,y:n}}},{key:"handleBarTooltip",value:function(t){var e,i,a=t.e,s=t.opt,r=this.w,n=this.ttCtx,o=n.getElTooltip(),l=0,h=0,c=0,d=this.getBarTooltipXY({e:a,opt:s});e=d.i;var g=d.barHeight,u=d.j;if(r.globals.capturedSeriesIndex=e,r.globals.capturedDataPointIndex=u,r.globals.isBarHorizontal&&n.tooltipUtil.hasBars()||!r.config.tooltip.shared?(h=d.x,c=d.y,i=Array.isArray(r.config.stroke.width)?r.config.stroke.width[e]:r.config.stroke.width,l=h):r.globals.comboCharts||r.config.tooltip.shared||(l/=2),isNaN(c)?c=r.globals.svgHeight-n.tooltipRect.ttHeight:c<0&&(c=0),h+n.tooltipRect.ttWidth>r.globals.gridWidth?h-=n.tooltipRect.ttWidth:h<0&&(h=0),n.w.config.tooltip.followCursor){var f=n.getElGrid().getBoundingClientRect();c=n.e.clientY-f.top}if(null===n.tooltip&&(n.tooltip=r.globals.dom.baseEl.querySelector(".apexcharts-tooltip")),r.config.tooltip.shared||(r.globals.comboBarCount>0?n.tooltipPosition.moveXCrosshairs(l+i/2):n.tooltipPosition.moveXCrosshairs(l)),!n.fixedTooltip&&(!r.config.tooltip.shared||r.globals.isBarHorizontal&&n.tooltipUtil.hasBars())){var p=r.globals.isMultipleYAxis?r.config.yaxis[x]&&r.config.yaxis[x].reversed:r.config.yaxis[0].reversed;p&&(h-=n.tooltipRect.ttWidth)<0&&(h=0),o.style.left=h+r.globals.translateX+"px";var x=parseInt(s.paths.parentNode.getAttribute("data:realIndex"),10);!p||r.globals.isBarHorizontal&&n.tooltipUtil.hasBars()||(c=c+g-2*(r.globals.series[e][u]<0?g:0)),n.tooltipRect.ttHeight+c>r.globals.gridHeight?(c=r.globals.gridHeight-n.tooltipRect.ttHeight+r.globals.translateY,o.style.top=c+"px"):o.style.top=c+r.globals.translateY-n.tooltipRect.ttHeight/2+"px"}}},{key:"getBarTooltipXY",value:function(t){var e=t.e,i=t.opt,a=this.w,s=null,r=this.ttCtx,n=0,o=0,l=0,h=0,c=0,d=e.target.classList;if(d.contains("apexcharts-bar-area")||d.contains("apexcharts-candlestick-area")||d.contains("apexcharts-rangebar-area")){var g=e.target,u=g.getBoundingClientRect(),f=i.elGrid.getBoundingClientRect(),p=u.height;c=u.height;var x=u.width,b=parseInt(g.getAttribute("cx"),10),m=parseInt(g.getAttribute("cy"),10);h=parseFloat(g.getAttribute("barWidth"));var v="touchmove"===e.type?e.touches[0].clientX:e.clientX;s=parseInt(g.getAttribute("j"),10),n=parseInt(g.parentNode.getAttribute("rel"),10)-1;var y=g.getAttribute("data-range-y1"),w=g.getAttribute("data-range-y2");a.globals.comboCharts&&(n=parseInt(g.parentNode.getAttribute("data:realIndex"),10)),r.tooltipLabels.drawSeriesTexts({ttItems:i.ttItems,i:n,j:s,y1:y?parseInt(y,10):null,y2:w?parseInt(w,10):null,shared:!r.showOnIntersect&&a.config.tooltip.shared}),a.config.tooltip.followCursor?a.globals.isBarHorizontal?(o=v-f.left+15,l=m-r.dataPointsDividedHeight+p/2-r.tooltipRect.ttHeight/2):(o=a.globals.isXNumeric?b-x/2:b-r.dataPointsDividedWidth+x/2,l=e.clientY-f.top-r.tooltipRect.ttHeight/2-15):a.globals.isBarHorizontal?((o=b)<r.xyRatios.baseLineInvertedY&&(o=b-r.tooltipRect.ttWidth),l=m-r.dataPointsDividedHeight+p/2-r.tooltipRect.ttHeight/2):(o=a.globals.isXNumeric?b-x/2:b-r.dataPointsDividedWidth+x/2,l=m)}return{x:o,y:l,barHeight:c,barWidth:h,i:n,j:s}}}]),t}(),mt=function(){function t(i){e(this,t),this.w=i.w,this.ttCtx=i}return a(t,[{key:"drawXaxisTooltip",value:function(){var t=this.w,e=this.ttCtx,i="bottom"===t.config.xaxis.position;e.xaxisOffY=i?t.globals.gridHeight+1:-t.globals.xAxisHeight-t.config.xaxis.axisTicks.height+3;var a=i?"apexcharts-xaxistooltip apexcharts-xaxistooltip-bottom":"apexcharts-xaxistooltip apexcharts-xaxistooltip-top",s=t.globals.dom.elWrap;e.blxaxisTooltip&&(null===t.globals.dom.baseEl.querySelector(".apexcharts-xaxistooltip")&&(e.xaxisTooltip=document.createElement("div"),e.xaxisTooltip.setAttribute("class",a+" apexcharts-theme-"+t.config.tooltip.theme),s.appendChild(e.xaxisTooltip),e.xaxisTooltipText=document.createElement("div"),e.xaxisTooltipText.classList.add("apexcharts-xaxistooltip-text"),e.xaxisTooltipText.style.fontFamily=t.config.xaxis.tooltip.style.fontFamily||t.config.chart.fontFamily,e.xaxisTooltipText.style.fontSize=t.config.xaxis.tooltip.style.fontSize,e.xaxisTooltip.appendChild(e.xaxisTooltipText)))}},{key:"drawYaxisTooltip",value:function(){for(var t=this.w,e=this.ttCtx,i=function(i){var a=t.config.yaxis[i].opposite||t.config.yaxis[i].crosshairs.opposite;e.yaxisOffX=a?t.globals.gridWidth+1:1;var s="apexcharts-yaxistooltip apexcharts-yaxistooltip-".concat(i,a?" apexcharts-yaxistooltip-right":" apexcharts-yaxistooltip-left");t.globals.yAxisSameScaleIndices.map((function(e,a){e.map((function(e,a){a===i&&(s+=t.config.yaxis[a].show?" ":" apexcharts-yaxistooltip-hidden")}))}));var r=t.globals.dom.elWrap;null===t.globals.dom.baseEl.querySelector(".apexcharts-yaxistooltip apexcharts-yaxistooltip-".concat(i))&&(e.yaxisTooltip=document.createElement("div"),e.yaxisTooltip.setAttribute("class",s+" apexcharts-theme-"+t.config.tooltip.theme),r.appendChild(e.yaxisTooltip),0===i&&(e.yaxisTooltipText=[]),e.yaxisTooltipText[i]=document.createElement("div"),e.yaxisTooltipText[i].classList.add("apexcharts-yaxistooltip-text"),e.yaxisTooltip.appendChild(e.yaxisTooltipText[i]))},a=0;a<t.config.yaxis.length;a++)i(a)}},{key:"setXCrosshairWidth",value:function(){var t=this.w,e=this.ttCtx,i=e.getElXCrosshairs();if(e.xcrosshairsWidth=parseInt(t.config.xaxis.crosshairs.width,10),t.globals.comboCharts){var a=t.globals.dom.baseEl.querySelector(".apexcharts-bar-area");if(null!==a&&"barWidth"===t.config.xaxis.crosshairs.width){var s=parseFloat(a.getAttribute("barWidth"));e.xcrosshairsWidth=s}else if("tickWidth"===t.config.xaxis.crosshairs.width){var r=t.globals.labels.length;e.xcrosshairsWidth=t.globals.gridWidth/r}}else if("tickWidth"===t.config.xaxis.crosshairs.width){var n=t.globals.labels.length;e.xcrosshairsWidth=t.globals.gridWidth/n}else if("barWidth"===t.config.xaxis.crosshairs.width){var o=t.globals.dom.baseEl.querySelector(".apexcharts-bar-area");if(null!==o){var l=parseFloat(o.getAttribute("barWidth"));e.xcrosshairsWidth=l}else e.xcrosshairsWidth=1}t.globals.isBarHorizontal&&(e.xcrosshairsWidth=0),null!==i&&e.xcrosshairsWidth>0&&i.setAttribute("width",e.xcrosshairsWidth)}},{key:"handleYCrosshair",value:function(){var t=this.w,e=this.ttCtx;e.ycrosshairs=t.globals.dom.baseEl.querySelector(".apexcharts-ycrosshairs"),e.ycrosshairsHidden=t.globals.dom.baseEl.querySelector(".apexcharts-ycrosshairs-hidden")}},{key:"drawYaxisTooltipText",value:function(t,e,i){var a=this.ttCtx,s=this.w,r=s.globals.yLabelFormatters[t];if(a.yaxisTooltips[t]){var n=a.getElGrid().getBoundingClientRect(),o=(e-n.top)*i.yRatio[t],l=s.globals.maxYArr[t]-s.globals.minYArr[t],h=s.globals.minYArr[t]+(l-o);a.tooltipPosition.moveYCrosshairs(e-n.top),a.yaxisTooltipText[t].innerHTML=r(h),a.tooltipPosition.moveYAxisTooltip(t)}}}]),t}(),vt=function(){function t(i){e(this,t),this.ctx=i,this.w=i.w;var a=this.w;this.tConfig=a.config.tooltip,this.tooltipUtil=new ut(this),this.tooltipLabels=new ft(this),this.tooltipPosition=new pt(this),this.marker=new xt(this),this.intersect=new bt(this),this.axesTooltip=new mt(this),this.showOnIntersect=this.tConfig.intersect,this.showTooltipTitle=this.tConfig.x.show,this.fixedTooltip=this.tConfig.fixed.enabled,this.xaxisTooltip=null,this.yaxisTTEls=null,this.isBarShared=!a.globals.isBarHorizontal&&this.tConfig.shared}return a(t,[{key:"getElTooltip",value:function(t){return t||(t=this),t.w.globals.dom.baseEl.querySelector(".apexcharts-tooltip")}},{key:"getElXCrosshairs",value:function(){return this.w.globals.dom.baseEl.querySelector(".apexcharts-xcrosshairs")}},{key:"getElGrid",value:function(){return this.w.globals.dom.baseEl.querySelector(".apexcharts-grid")}},{key:"drawTooltip",value:function(t){var e=this.w;this.xyRatios=t,this.blxaxisTooltip=e.config.xaxis.tooltip.enabled&&e.globals.axisCharts,this.yaxisTooltips=e.config.yaxis.map((function(t,i){return!!(t.show&&t.tooltip.enabled&&e.globals.axisCharts)})),this.allTooltipSeriesGroups=[],e.globals.axisCharts||(this.showTooltipTitle=!1);var i=document.createElement("div");if(i.classList.add("apexcharts-tooltip"),i.classList.add("apexcharts-theme-".concat(this.tConfig.theme)),e.globals.dom.elWrap.appendChild(i),e.globals.axisCharts){this.axesTooltip.drawXaxisTooltip(),this.axesTooltip.drawYaxisTooltip(),this.axesTooltip.setXCrosshairWidth(),this.axesTooltip.handleYCrosshair();var a=new j(this.ctx);this.xAxisTicksPositions=a.getXAxisTicksPositions()}if(!e.globals.comboCharts&&!this.tConfig.intersect&&"bar"!==e.config.chart.type&&"rangeBar"!==e.config.chart.type||this.tConfig.shared||(this.showOnIntersect=!0),0!==e.config.markers.size&&0!==e.globals.markers.largestSize||this.marker.drawDynamicPoints(this),e.globals.collapsedSeries.length!==e.globals.series.length){this.dataPointsDividedHeight=e.globals.gridHeight/e.globals.dataPoints,this.dataPointsDividedWidth=e.globals.gridWidth/e.globals.dataPoints,this.showTooltipTitle&&(this.tooltipTitle=document.createElement("div"),this.tooltipTitle.classList.add("apexcharts-tooltip-title"),this.tooltipTitle.style.fontFamily=this.tConfig.style.fontFamily||e.config.chart.fontFamily,this.tooltipTitle.style.fontSize=this.tConfig.style.fontSize,i.appendChild(this.tooltipTitle));var s=e.globals.series.length;(e.globals.xyCharts||e.globals.comboCharts)&&this.tConfig.shared&&(s=this.showOnIntersect?1:e.globals.series.length),this.legendLabels=e.globals.dom.baseEl.querySelectorAll(".apexcharts-legend-text"),this.ttItems=this.createTTElements(s),this.addSVGEvents()}}},{key:"createTTElements",value:function(t){for(var e=this.w,i=[],a=this.getElTooltip(),s=0;s<t;s++){var r=document.createElement("div");r.classList.add("apexcharts-tooltip-series-group"),this.tConfig.shared&&this.tConfig.enabledOnSeries&&Array.isArray(this.tConfig.enabledOnSeries)&&this.tConfig.enabledOnSeries.indexOf(s)<0&&r.classList.add("apexcharts-tooltip-series-group-hidden");var n=document.createElement("span");n.classList.add("apexcharts-tooltip-marker"),n.style.backgroundColor=e.globals.colors[s],r.appendChild(n);var o=document.createElement("div");o.classList.add("apexcharts-tooltip-text"),o.style.fontFamily=this.tConfig.style.fontFamily||e.config.chart.fontFamily,o.style.fontSize=this.tConfig.style.fontSize;var l=document.createElement("div");l.classList.add("apexcharts-tooltip-y-group");var h=document.createElement("span");h.classList.add("apexcharts-tooltip-text-label"),l.appendChild(h);var c=document.createElement("span");c.classList.add("apexcharts-tooltip-text-value"),l.appendChild(c);var d=document.createElement("div");d.classList.add("apexcharts-tooltip-z-group");var g=document.createElement("span");g.classList.add("apexcharts-tooltip-text-z-label"),d.appendChild(g);var u=document.createElement("span");u.classList.add("apexcharts-tooltip-text-z-value"),d.appendChild(u),o.appendChild(l),o.appendChild(d),r.appendChild(o),a.appendChild(r),i.push(r)}return i}},{key:"addSVGEvents",value:function(){var t=this.w,e=t.config.chart.type,i=this.getElTooltip(),a=!("bar"!==e&&"candlestick"!==e&&"rangeBar"!==e),s="area"===e||"line"===e||"scatter"===e||"bubble"===e||"radar"===e,r=t.globals.dom.Paper.node,n=this.getElGrid();n&&(this.seriesBound=n.getBoundingClientRect());var o,l=[],h=[],c={hoverArea:r,elGrid:n,tooltipEl:i,tooltipY:l,tooltipX:h,ttItems:this.ttItems};if(t.globals.axisCharts&&(s?o=t.globals.dom.baseEl.querySelectorAll(".apexcharts-series[data\\:longestSeries='true'] .apexcharts-marker"):a?o=t.globals.dom.baseEl.querySelectorAll(".apexcharts-series .apexcharts-bar-area, .apexcharts-series .apexcharts-candlestick-area, .apexcharts-series .apexcharts-rangebar-area"):"heatmap"===e&&(o=t.globals.dom.baseEl.querySelectorAll(".apexcharts-series .apexcharts-heatmap")),o&&o.length))for(var d=0;d<o.length;d++)l.push(o[d].getAttribute("cy")),h.push(o[d].getAttribute("cx"));if(t.globals.xyCharts&&!this.showOnIntersect||t.globals.comboCharts&&!this.showOnIntersect||a&&this.tooltipUtil.hasBars()&&this.tConfig.shared)this.addPathsEventListeners([r],c);else if(a&&!t.globals.comboCharts||s&&this.showOnIntersect)this.addDatapointEventsListeners(c);else if(!t.globals.axisCharts||"heatmap"===e){var g=t.globals.dom.baseEl.querySelectorAll(".apexcharts-series");this.addPathsEventListeners(g,c)}if(this.showOnIntersect){var u=t.globals.dom.baseEl.querySelectorAll(".apexcharts-line-series .apexcharts-marker, .apexcharts-area-series .apexcharts-marker");u.length>0&&this.addPathsEventListeners(u,c),this.tooltipUtil.hasBars()&&!this.tConfig.shared&&this.addDatapointEventsListeners(c)}}},{key:"drawFixedTooltipRect",value:function(){var t=this.w,e=this.getElTooltip(),i=e.getBoundingClientRect(),a=i.width+10,s=i.height+10,r=this.tConfig.fixed.offsetX,n=this.tConfig.fixed.offsetY,o=this.tConfig.fixed.position.toLowerCase();return o.indexOf("right")>-1&&(r=r+t.globals.svgWidth-a+10),o.indexOf("bottom")>-1&&(n=n+t.globals.svgHeight-s-10),e.style.left=r+"px",e.style.top=n+"px",{x:r,y:n,ttWidth:a,ttHeight:s}}},{key:"addDatapointEventsListeners",value:function(t){var e=this.w.globals.dom.baseEl.querySelectorAll(".apexcharts-series-markers .apexcharts-marker, .apexcharts-bar-area, .apexcharts-candlestick-area, .apexcharts-rangebar-area");this.addPathsEventListeners(e,t)}},{key:"addPathsEventListeners",value:function(t,e){for(var i=this,a=function(a){var s={paths:t[a],tooltipEl:e.tooltipEl,tooltipY:e.tooltipY,tooltipX:e.tooltipX,elGrid:e.elGrid,hoverArea:e.hoverArea,ttItems:e.ttItems};["mousemove","mouseup","touchmove","mouseout","touchend"].map((function(e){return t[a].addEventListener(e,i.seriesHover.bind(i,s),{capture:!1,passive:!0})}))},s=0;s<t.length;s++)a(s)}},{key:"seriesHover",value:function(t,e){var i=this,a=[],s=this.w;s.config.chart.group&&(a=this.ctx.getGroupedCharts()),s.globals.axisCharts&&(s.globals.minX===-1/0&&s.globals.maxX===1/0||0===s.globals.dataPoints)||(a.length?a.forEach((function(a){var s=i.getElTooltip(a),r={paths:t.paths,tooltipEl:s,tooltipY:t.tooltipY,tooltipX:t.tooltipX,elGrid:t.elGrid,hoverArea:t.hoverArea,ttItems:a.w.globals.tooltip.ttItems};a.w.globals.minX===i.w.globals.minX&&a.w.globals.maxX===i.w.globals.maxX&&a.w.globals.tooltip.seriesHoverByContext({chartCtx:a,ttCtx:a.w.globals.tooltip,opt:r,e:e})})):this.seriesHoverByContext({chartCtx:this.ctx,ttCtx:this.w.globals.tooltip,opt:t,e:e}))}},{key:"seriesHoverByContext",value:function(t){var e=t.chartCtx,i=t.ttCtx,a=t.opt,s=t.e,r=e.w,n=this.getElTooltip();(i.tooltipRect={x:0,y:0,ttWidth:n.getBoundingClientRect().width,ttHeight:n.getBoundingClientRect().height},i.e=s,!i.tooltipUtil.hasBars()||r.globals.comboCharts||i.isBarShared)||this.tConfig.onDatasetHover.highlightDataSeries&&new X(e).toggleSeriesOnHover(s,s.target.parentNode);i.fixedTooltip&&i.drawFixedTooltipRect(),r.globals.axisCharts?i.axisChartsTooltips({e:s,opt:a,tooltipRect:i.tooltipRect}):i.nonAxisChartsTooltips({e:s,opt:a,tooltipRect:i.tooltipRect})}},{key:"axisChartsTooltips",value:function(t){var e,i,a=t.e,s=t.opt,r=this.w,n=s.elGrid.getBoundingClientRect(),o="touchmove"===a.type?a.touches[0].clientX:a.clientX,l="touchmove"===a.type?a.touches[0].clientY:a.clientY;if(this.clientY=l,this.clientX=o,r.globals.capturedSeriesIndex=-1,r.globals.capturedDataPointIndex=-1,l<n.top||l>n.top+n.height)this.handleMouseOut(s);else{if(Array.isArray(this.tConfig.enabledOnSeries)&&!r.config.tooltip.shared){var h=parseInt(s.paths.getAttribute("index"),10);if(this.tConfig.enabledOnSeries.indexOf(h)<0)return void this.handleMouseOut(s)}var c=this.getElTooltip(),d=this.getElXCrosshairs(),g=r.globals.xyCharts||"bar"===r.config.chart.type&&!r.globals.isBarHorizontal&&this.tooltipUtil.hasBars()&&this.tConfig.shared||r.globals.comboCharts&&this.tooltipUtil.hasBars();if(r.globals.isBarHorizontal&&this.tooltipUtil.hasBars()&&(g=!1),"mousemove"===a.type||"touchmove"===a.type||"mouseup"===a.type){null!==d&&d.classList.add("apexcharts-active");var u=this.yaxisTooltips.filter((function(t){return!0===t}));if(null!==this.ycrosshairs&&u.length&&this.ycrosshairs.classList.add("apexcharts-active"),g&&!this.showOnIntersect)this.handleStickyTooltip(a,o,l,s);else if("heatmap"===r.config.chart.type){var f=this.intersect.handleHeatTooltip({e:a,opt:s,x:e,y:i});e=f.x,i=f.y,c.style.left=e+"px",c.style.top=i+"px"}else this.tooltipUtil.hasBars()&&this.intersect.handleBarTooltip({e:a,opt:s}),this.tooltipUtil.hasMarkers()&&this.intersect.handleMarkerTooltip({e:a,opt:s,x:e,y:i});if(this.yaxisTooltips.length)for(var p=0;p<r.config.yaxis.length;p++)this.axesTooltip.drawYaxisTooltipText(p,l,this.xyRatios);s.tooltipEl.classList.add("apexcharts-active")}else"mouseout"!==a.type&&"touchend"!==a.type||this.handleMouseOut(s)}}},{key:"nonAxisChartsTooltips",value:function(t){var e=t.e,i=t.opt,a=t.tooltipRect,s=this.w,r=i.paths.getAttribute("rel"),n=this.getElTooltip(),o=s.globals.dom.elWrap.getBoundingClientRect();if("mousemove"===e.type||"touchmove"===e.type){n.classList.add("apexcharts-active"),this.tooltipLabels.drawSeriesTexts({ttItems:i.ttItems,i:parseInt(r,10)-1,shared:!1});var l=s.globals.clientX-o.left-a.ttWidth/2,h=s.globals.clientY-o.top-a.ttHeight-10;n.style.left=l+"px",n.style.top=h+"px"}else"mouseout"!==e.type&&"touchend"!==e.type||n.classList.remove("apexcharts-active")}},{key:"handleStickyTooltip",value:function(t,e,i,a){var s=this.w,r=this.tooltipUtil.getNearestValues({context:this,hoverArea:a.hoverArea,elGrid:a.elGrid,clientX:e,clientY:i}),n=r.j,o=r.capturedSeries;r.hoverX<0||r.hoverX>s.globals.gridWidth?this.handleMouseOut(a):null!==o?this.handleStickyCapturedSeries(t,o,a,n):this.tooltipUtil.isXoverlap(n)&&this.create(t,this,0,n,a.ttItems)}},{key:"handleStickyCapturedSeries",value:function(t,e,i,a){var s=this.w;null===s.globals.series[e][a]?this.handleMouseOut(i):void 0!==s.globals.series[e][a]?this.tConfig.shared&&this.tooltipUtil.isXoverlap(a)&&this.tooltipUtil.isInitialSeriesSameLen()?this.create(t,this,e,a,i.ttItems):this.create(t,this,e,a,i.ttItems,!1):this.tooltipUtil.isXoverlap(a)&&this.create(t,this,0,a,i.ttItems)}},{key:"deactivateHoverFilter",value:function(){for(var t=this.w,e=new b(this.ctx),i=t.globals.dom.Paper.select(".apexcharts-bar-area"),a=0;a<i.length;a++)e.pathMouseLeave(i[a])}},{key:"handleMouseOut",value:function(t){var e=this.w,i=this.getElXCrosshairs();if(t.tooltipEl.classList.remove("apexcharts-active"),this.deactivateHoverFilter(),"bubble"!==e.config.chart.type&&this.marker.resetPointsSize(),null!==i&&i.classList.remove("apexcharts-active"),null!==this.ycrosshairs&&this.ycrosshairs.classList.remove("apexcharts-active"),this.blxaxisTooltip&&this.xaxisTooltip.classList.remove("apexcharts-active"),this.yaxisTooltips.length){null===this.yaxisTTEls&&(this.yaxisTTEls=e.globals.dom.baseEl.querySelectorAll(".apexcharts-yaxistooltip"));for(var a=0;a<this.yaxisTTEls.length;a++)this.yaxisTTEls[a].classList.remove("apexcharts-active")}e.config.legend.tooltipHoverFormatter&&this.legendLabels.forEach((function(t){var e=t.getAttribute("data:default-text");t.innerHTML=decodeURIComponent(e)}))}},{key:"markerClick",value:function(t,e,i){var a=this.w;"function"==typeof a.config.chart.events.markerClick&&a.config.chart.events.markerClick(t,this.ctx,{seriesIndex:e,dataPointIndex:i,w:a}),this.ctx.events.fireEvent("markerClick",[t,this.ctx,{seriesIndex:e,dataPointIndex:i,w:a}])}},{key:"create",value:function(t,e,i,a,s){var r=arguments.length>5&&void 0!==arguments[5]?arguments[5]:null,n=this.w,o=e;"mouseup"===t.type&&this.markerClick(t,i,a),null===r&&(r=this.tConfig.shared);var l=this.tooltipUtil.hasMarkers(),h=this.tooltipUtil.getElBars();if(n.config.legend.tooltipHoverFormatter){var c=n.config.legend.tooltipHoverFormatter,d=Array.from(this.legendLabels);d.forEach((function(t){var e=t.getAttribute("data:default-text");t.innerHTML=decodeURIComponent(e)}));for(var g=0;g<d.length;g++){var u=d[g],f=parseInt(u.getAttribute("i"),10),p=decodeURIComponent(u.getAttribute("data:default-text")),x=c(p,{seriesIndex:r?f:i,dataPointIndex:a,w:n});if(r)u.innerHTML=n.globals.collapsedSeriesIndices.indexOf(f)<0?x:p;else if(u.innerHTML=f===i?x:p,i===f)break}}if(r){if(o.tooltipLabels.drawSeriesTexts({ttItems:s,i:i,j:a,shared:!this.showOnIntersect&&this.tConfig.shared}),l&&(n.globals.markers.largestSize>0?o.marker.enlargePoints(a):o.tooltipPosition.moveDynamicPointsOnHover(a)),this.tooltipUtil.hasBars()&&(this.barSeriesHeight=this.tooltipUtil.getBarsHeight(h),this.barSeriesHeight>0)){var m=new b(this.ctx),v=n.globals.dom.Paper.select(".apexcharts-bar-area[j='".concat(a,"']"));this.deactivateHoverFilter(),this.tooltipPosition.moveStickyTooltipOverBars(a);for(var y=0;y<v.length;y++)m.pathMouseEnter(v[y])}}else o.tooltipLabels.drawSeriesTexts({shared:!1,ttItems:s,i:i,j:a}),this.tooltipUtil.hasBars()&&o.tooltipPosition.moveStickyTooltipOverBars(a),l&&o.tooltipPosition.moveMarkers(i,a)}}]),t}(),yt=function(t){o(s,t);var i=d(s);function s(){return e(this,s),i.apply(this,arguments)}return a(s,[{key:"draw",value:function(t,e){var i=this,a=this.w;this.graphics=new b(this.ctx),this.bar=new F(this.ctx,this.xyRatios);var s=new y(this.ctx,a);t=s.getLogSeries(t),this.yRatio=s.getLogYRatios(this.yRatio),this.barHelpers.initVariables(t),"100%"===a.config.chart.stackType&&(t=a.globals.seriesPercent.slice()),this.series=t,this.totalItems=0,this.prevY=[],this.prevX=[],this.prevYF=[],this.prevXF=[],this.prevYVal=[],this.prevXVal=[],this.xArrj=[],this.xArrjF=[],this.xArrjVal=[],this.yArrj=[],this.yArrjF=[],this.yArrjVal=[];for(var r=0;r<t.length;r++)t[r].length>0&&(this.totalItems+=t[r].length);for(var o=this.graphics.group({class:"apexcharts-bar-series apexcharts-plot-series"}),l=0,h=0,c=function(s,r){var c=void 0,d=void 0,g=void 0,u=void 0,p=[],x=[],b=a.globals.comboCharts?e[s]:s;i.yRatio.length>1&&(i.yaxisIndex=b),i.isReversed=a.config.yaxis[i.yaxisIndex]&&a.config.yaxis[i.yaxisIndex].reversed;var m=i.graphics.group({class:"apexcharts-series",seriesName:f.escapeString(a.globals.seriesNames[b]),rel:s+1,"data:realIndex":b});i.ctx.series.addCollapsedClassToSeries(m,b);var v=i.graphics.group({class:"apexcharts-datalabels","data:realIndex":b}),y=0,w=0,k=i.initialPositions(l,h,c,d,g,u);h=k.y,y=k.barHeight,d=k.yDivision,u=k.zeroW,l=k.x,w=k.barWidth,c=k.xDivision,g=k.zeroH,i.yArrj=[],i.yArrjF=[],i.yArrjVal=[],i.xArrj=[],i.xArrjF=[],i.xArrjVal=[],1===i.prevY.length&&i.prevY[0].every((function(t){return isNaN(t)}))&&(i.prevY[0]=i.prevY[0].map((function(t){return g})),i.prevYF[0]=i.prevYF[0].map((function(t){return 0})));for(var A=0;A<a.globals.dataPoints;A++){var S=i.barHelpers.getStrokeWidth(s,A,b),C={indexes:{i:s,j:A,realIndex:b,bc:r},strokeWidth:S,x:l,y:h,elSeries:m},L=null;i.isHorizontal?(L=i.drawStackedBarPaths(n(n({},C),{},{zeroW:u,barHeight:y,yDivision:d})),w=i.series[s][A]/i.invertedYRatio):(L=i.drawStackedColumnPaths(n(n({},C),{},{xDivision:c,barWidth:w,zeroH:g})),y=i.series[s][A]/i.yRatio[i.yaxisIndex]),h=L.y,l=L.x,p.push(l),x.push(h);var P=i.barHelpers.getPathFillColor(t,s,A,b);m=i.renderSeries({realIndex:b,pathFill:P,j:A,i:s,pathFrom:L.pathFrom,pathTo:L.pathTo,strokeWidth:S,elSeries:m,x:l,y:h,series:t,barHeight:y,barWidth:w,elDataLabelsWrap:v,type:"bar",visibleSeries:0})}a.globals.seriesXvalues[b]=p,a.globals.seriesYvalues[b]=x,i.prevY.push(i.yArrj),i.prevYF.push(i.yArrjF),i.prevYVal.push(i.yArrjVal),i.prevX.push(i.xArrj),i.prevXF.push(i.xArrjF),i.prevXVal.push(i.xArrjVal),o.add(m)},d=0,g=0;d<t.length;d++,g++)c(d,g);return o}},{key:"initialPositions",value:function(t,e,i,a,s,r){var n,o,l=this.w;return this.isHorizontal?(n=(n=a=l.globals.gridHeight/l.globals.dataPoints)*parseInt(l.config.plotOptions.bar.barHeight,10)/100,r=this.baseLineInvertedY+l.globals.padHorizontal+(this.isReversed?l.globals.gridWidth:0)-(this.isReversed?2*this.baseLineInvertedY:0),e=(a-n)/2):(o=i=l.globals.gridWidth/l.globals.dataPoints,o=l.globals.isXNumeric&&l.globals.dataPoints>1?(i=l.globals.minXDiff/this.xRatio)*parseInt(this.barOptions.columnWidth,10)/100:o*parseInt(l.config.plotOptions.bar.columnWidth,10)/100,s=this.baseLineY[this.yaxisIndex]+(this.isReversed?l.globals.gridHeight:0)-(this.isReversed?2*this.baseLineY[this.yaxisIndex]:0),t=l.globals.padHorizontal+(i-o)/2),{x:t,y:e,yDivision:a,xDivision:i,barHeight:n,barWidth:o,zeroH:s,zeroW:r}}},{key:"drawStackedBarPaths",value:function(t){for(var e,i=t.indexes,a=t.barHeight,s=t.strokeWidth,r=t.zeroW,n=t.x,o=t.y,l=t.yDivision,h=t.elSeries,c=this.w,d=o,g=i.i,u=i.j,f=0,p=0;p<this.prevXF.length;p++)f+=this.prevXF[p][u];if(g>0){var x=r;this.prevXVal[g-1][u]<0?x=this.series[g][u]>=0?this.prevX[g-1][u]+f-2*(this.isReversed?f:0):this.prevX[g-1][u]:this.prevXVal[g-1][u]>=0&&(x=this.series[g][u]>=0?this.prevX[g-1][u]:this.prevX[g-1][u]-f+2*(this.isReversed?f:0)),e=x}else e=r;n=null===this.series[g][u]?e:e+this.series[g][u]/this.invertedYRatio-2*(this.isReversed?this.series[g][u]/this.invertedYRatio:0),this.xArrj.push(n),this.xArrjF.push(Math.abs(e-n)),this.xArrjVal.push(this.series[g][u]);var b=this.barHelpers.getBarpaths({barYPosition:d,barHeight:a,x1:e,x2:n,strokeWidth:s,series:this.series,realIndex:i.realIndex,i:g,j:u,w:c});return this.barHelpers.barBackground({j:u,i:g,y1:d,y2:a,elSeries:h}),o+=l,{pathTo:b.pathTo,pathFrom:b.pathFrom,x:n,y:o}}},{key:"drawStackedColumnPaths",value:function(t){var e=t.indexes,i=t.x,a=t.y,s=t.xDivision,r=t.barWidth,n=t.zeroH,o=(t.strokeWidth,t.elSeries),l=this.w,h=e.i,c=e.j,d=e.bc;if(l.globals.isXNumeric){var g=l.globals.seriesX[h][c];g||(g=0),i=(g-l.globals.minX)/this.xRatio-r/2}for(var u,f=i,p=0,x=0;x<this.prevYF.length;x++)p+=isNaN(this.prevYF[x][c])?0:this.prevYF[x][c];if(h>0&&!l.globals.isXNumeric||h>0&&l.globals.isXNumeric&&l.globals.seriesX[h-1][c]===l.globals.seriesX[h][c]){var b,m,v=Math.min(this.yRatio.length+1,h+1);if(void 0!==this.prevY[h-1])for(var y=1;y<v;y++)if(!isNaN(this.prevY[h-y][c])){m=this.prevY[h-y][c];break}for(var w=1;w<v;w++){if(this.prevYVal[h-w][c]<0){b=this.series[h][c]>=0?m-p+2*(this.isReversed?p:0):m;break}if(this.prevYVal[h-w][c]>=0){b=this.series[h][c]>=0?m:m+p-2*(this.isReversed?p:0);break}}void 0===b&&(b=l.globals.gridHeight),u=this.prevYF[0].every((function(t){return 0===t}))&&this.prevYF.slice(1,h).every((function(t){return t.every((function(t){return isNaN(t)}))}))?l.globals.gridHeight-n:b}else u=l.globals.gridHeight-n;a=u-this.series[h][c]/this.yRatio[this.yaxisIndex]+2*(this.isReversed?this.series[h][c]/this.yRatio[this.yaxisIndex]:0),this.yArrj.push(a),this.yArrjF.push(Math.abs(u-a)),this.yArrjVal.push(this.series[h][c]);var k=this.barHelpers.getColumnPaths({barXPosition:f,barWidth:r,y1:u,y2:a,yRatio:this.yRatio[this.yaxisIndex],strokeWidth:this.strokeWidth,series:this.series,realIndex:e.realIndex,i:h,j:c,w:l});return this.barHelpers.barBackground({bc:d,j:c,i:h,x1:f,x2:r,elSeries:o}),i+=s,{pathTo:k.pathTo,pathFrom:k.pathFrom,x:l.globals.isXNumeric?i-s:i,y:a}}}]),s}(F),wt=function(t){o(s,t);var i=d(s);function s(){return e(this,s),i.apply(this,arguments)}return a(s,[{key:"draw",value:function(t,e){var i=this.w,a=new b(this.ctx),s=new T(this.ctx);this.candlestickOptions=this.w.config.plotOptions.candlestick;var r=new y(this.ctx,i);t=r.getLogSeries(t),this.series=t,this.yRatio=r.getLogYRatios(this.yRatio),this.barHelpers.initVariables(t);for(var n=a.group({class:"apexcharts-candlestick-series apexcharts-plot-series"}),o=0;o<t.length;o++){var l,h,c=void 0,d=void 0,g=[],u=[],p=i.globals.comboCharts?e[o]:o,x=a.group({class:"apexcharts-series",seriesName:f.escapeString(i.globals.seriesNames[p]),rel:o+1,"data:realIndex":p});t[o].length>0&&(this.visibleI=this.visibleI+1);var m,v;this.yRatio.length>1&&(this.yaxisIndex=p);var w=this.barHelpers.initialPositions();d=w.y,m=w.barHeight,c=w.x,v=w.barWidth,l=w.xDivision,h=w.zeroH,u.push(c+v/2);for(var k=a.group({class:"apexcharts-datalabels","data:realIndex":p}),A=0;A<i.globals.dataPoints;A++){var S,C=this.barHelpers.getStrokeWidth(o,A,p),L=this.drawCandleStickPaths({indexes:{i:o,j:A,realIndex:p},x:c,y:d,xDivision:l,barWidth:v,zeroH:h,strokeWidth:C,elSeries:x});d=L.y,c=L.x,S=L.color,A>0&&u.push(c+v/2),g.push(d);var P=s.fillPath({seriesNumber:p,dataPointIndex:A,color:S,value:t[o][A]}),z=this.candlestickOptions.wick.useFillColor?S:void 0;this.renderSeries({realIndex:p,pathFill:P,lineFill:z,j:A,i:o,pathFrom:L.pathFrom,pathTo:L.pathTo,strokeWidth:C,elSeries:x,x:c,y:d,series:t,barHeight:m,barWidth:v,elDataLabelsWrap:k,visibleSeries:this.visibleI,type:"candlestick"})}i.globals.seriesXvalues[p]=u,i.globals.seriesYvalues[p]=g,n.add(x)}return n}},{key:"drawCandleStickPaths",value:function(t){var e=t.indexes,i=t.x,a=(t.y,t.xDivision),s=t.barWidth,r=t.zeroH,n=t.strokeWidth,o=this.w,l=new b(this.ctx),h=e.i,c=e.j,d=!0,g=o.config.plotOptions.candlestick.colors.upward,u=o.config.plotOptions.candlestick.colors.downward,f=this.yRatio[this.yaxisIndex],p=e.realIndex,x=this.getOHLCValue(p,c),m=r,v=r;x.o>x.c&&(d=!1);var y=Math.min(x.o,x.c),w=Math.max(x.o,x.c);o.globals.isXNumeric&&(i=(o.globals.seriesX[p][c]-o.globals.minX)/this.xRatio-s/2);var k=i+s*this.visibleI;void 0===this.series[h][c]||null===this.series[h][c]?y=r:(y=r-y/f,w=r-w/f,m=r-x.h/f,v=r-x.l/f);var A=l.move(k,r),S=l.move(k,y);return o.globals.previousPaths.length>0&&(S=this.getPreviousPath(p,c,!0)),A=l.move(k,w)+l.line(k+s/2,w)+l.line(k+s/2,m)+l.line(k+s/2,w)+l.line(k+s,w)+l.line(k+s,y)+l.line(k+s/2,y)+l.line(k+s/2,v)+l.line(k+s/2,y)+l.line(k,y)+l.line(k,w-n/2),S+=l.move(k,y),o.globals.isXNumeric||(i+=a),{pathTo:A,pathFrom:S,x:i,y:w,barXPosition:k,color:d?g:u}}},{key:"getOHLCValue",value:function(t,e){var i=this.w;return{o:i.globals.seriesCandleO[t][e],h:i.globals.seriesCandleH[t][e],l:i.globals.seriesCandleL[t][e],c:i.globals.seriesCandleC[t][e]}}}]),s}(F),kt=function(){function t(i,a){e(this,t),this.ctx=i,this.w=i.w,this.xRatio=a.xRatio,this.yRatio=a.yRatio,this.negRange=!1,this.dynamicAnim=this.w.config.chart.animations.dynamicAnimation,this.rectRadius=this.w.config.plotOptions.heatmap.radius,this.strokeWidth=this.w.config.stroke.show?this.w.config.stroke.width:0}return a(t,[{key:"draw",value:function(t){var e=this.w,i=new b(this.ctx),a=i.group({class:"apexcharts-heatmap"});a.attr("clip-path","url(#gridRectMask".concat(e.globals.cuid,")"));var s=e.globals.gridWidth/e.globals.dataPoints,r=e.globals.gridHeight/e.globals.series.length,n=0,o=!1;this.checkColorRange();var l=t.slice();e.config.yaxis[0].reversed&&(o=!0,l.reverse());for(var h=o?0:l.length-1;o?h<l.length:h>=0;o?h++:h--){var c=i.group({class:"apexcharts-series apexcharts-heatmap-series",seriesName:f.escapeString(e.globals.seriesNames[h]),rel:h+1,"data:realIndex":h});if(this.ctx.series.addCollapsedClassToSeries(c,h),e.config.chart.dropShadow.enabled){var d=e.config.chart.dropShadow;new p(this.ctx).dropShadow(c,d,h)}for(var g=0,u=0;u<l[h].length;u++){var x=1,m=e.config.plotOptions.heatmap.shadeIntensity,v=this.determineHeatColor(h,u);x=e.globals.hasNegs||this.negRange?e.config.plotOptions.heatmap.reverseNegativeShade?v.percent<0?v.percent/100*(1.25*m):(1-v.percent/100)*(1.25*m):v.percent<=0?1-(1+v.percent/100)*m:(1-v.percent/100)*m:1-v.percent/100;var y=v.color,w=new f;if(e.config.plotOptions.heatmap.enableShades&&(x<0&&(x=0),y="dark"===this.w.config.theme.mode?f.hexToRgba(w.shadeColor(-1*x,v.color),e.config.fill.opacity):f.hexToRgba(w.shadeColor(x,v.color),e.config.fill.opacity)),"image"===e.config.fill.type)y=new T(this.ctx).fillPath({seriesNumber:h,dataPointIndex:u,opacity:e.globals.hasNegs?v.percent<0?1-(1+v.percent/100):m+v.percent/100:v.percent/100,patternID:f.randomId(),width:e.config.fill.image.width?e.config.fill.image.width:s,height:e.config.fill.image.height?e.config.fill.image.height:r});var k=this.rectRadius,A=i.drawRect(g,n,s,r,k);if(A.attr({cx:g,cy:n}),A.node.classList.add("apexcharts-heatmap-rect"),c.add(A),A.attr({fill:y,i:h,index:h,j:u,val:l[h][u],"stroke-width":this.strokeWidth,stroke:e.config.plotOptions.heatmap.useFillColorAsStroke?y:e.globals.stroke.colors[0],color:y}),A.node.addEventListener("mouseenter",i.pathMouseEnter.bind(this,A)),A.node.addEventListener("mouseleave",i.pathMouseLeave.bind(this,A)),A.node.addEventListener("mousedown",i.pathMouseDown.bind(this,A)),e.config.chart.animations.enabled&&!e.globals.dataChanged){var S=1;e.globals.resized||(S=e.config.chart.animations.speed),this.animateHeatMap(A,g,n,s,r,S)}if(e.globals.dataChanged){var C=1;if(this.dynamicAnim.enabled&&e.globals.shouldAnimate){C=this.dynamicAnim.speed;var L=e.globals.previousPaths[h]&&e.globals.previousPaths[h][u]&&e.globals.previousPaths[h][u].color;L||(L="rgba(255, 255, 255, 0)"),this.animateHeatColor(A,f.isColorHex(L)?L:f.rgb2hex(L),f.isColorHex(y)?y:f.rgb2hex(y),C)}}var P=this.calculateHeatmapDataLabels({x:g,y:n,i:h,j:u,heatColorProps:v,series:l,rectHeight:r,rectWidth:s});null!==P&&c.add(P),g+=s}n+=r,a.add(c)}var z=e.globals.yAxisScale[0].result.slice();e.config.yaxis[0].reversed?z.unshift(""):z.push(""),e.globals.yAxisScale[0].result=z;var I=e.globals.gridHeight/e.globals.series.length;return e.config.yaxis[0].labels.offsetY=-I/2,a}},{key:"checkColorRange",value:function(){var t=this,e=this.w.config.plotOptions.heatmap;e.colorScale.ranges.length>0&&e.colorScale.ranges.map((function(e,i){e.from<=0&&(t.negRange=!0)}))}},{key:"determineHeatColor",value:function(t,e){var i=this.w,a=i.globals.series[t][e],s=i.config.plotOptions.heatmap,r=s.colorScale.inverse?e:t,n=i.globals.colors[r],o=null,l=Math.min.apply(Math,g(i.globals.series[t])),h=Math.max.apply(Math,g(i.globals.series[t]));s.distributed||(l=i.globals.minY,h=i.globals.maxY),void 0!==s.colorScale.min&&(l=s.colorScale.min<i.globals.minY?s.colorScale.min:i.globals.minY,h=s.colorScale.max>i.globals.maxY?s.colorScale.max:i.globals.maxY);var c=Math.abs(h)+Math.abs(l),d=100*a/(0===c?c-1e-6:c);s.colorScale.ranges.length>0&&s.colorScale.ranges.map((function(t,e){if(a>=t.from&&a<=t.to){n=t.color,o=t.foreColor?t.foreColor:null,l=t.from,h=t.to;var i=Math.abs(h)+Math.abs(l);d=100*a/(0===i?i-1e-6:i)}}));return{color:n,foreColor:o,percent:d}}},{key:"calculateHeatmapDataLabels",value:function(t){var e=t.x,i=t.y,a=t.i,s=t.j,r=t.heatColorProps,n=(t.series,t.rectHeight),o=t.rectWidth,l=this.w,h=l.config.dataLabels,c=new b(this.ctx),d=new M(this.ctx),g=h.formatter,u=null;if(h.enabled){u=c.group({class:"apexcharts-data-labels"});var f=h.offsetX,p=h.offsetY,x=e+o/2+f,m=i+n/2+parseFloat(h.style.fontSize)/3+p,v=g(l.globals.series[a][s],{seriesIndex:a,dataPointIndex:s,w:l});d.plotDataLabelsText({x:x,y:m,text:v,i:a,j:s,color:r.foreColor,parent:u,dataLabelsConfig:h})}return u}},{key:"animateHeatMap",value:function(t,e,i,a,s,r){var n=new x(this.ctx);n.animateRect(t,{x:e+a/2,y:i+s/2,width:0,height:0},{x:e,y:i,width:a,height:s},r,(function(){n.animationCompleted(t)}))}},{key:"animateHeatColor",value:function(t,e,i,a){t.attr({fill:e}).animate(a).attr({fill:i})}}]),t}(),At=function(){function t(i){e(this,t),this.ctx=i,this.w=i.w}return a(t,[{key:"drawYAxisTexts",value:function(t,e,i,a){var s=this.w,r=s.config.yaxis[0],n=s.globals.yLabelFormatters[0];return new b(this.ctx).drawText({x:t+r.labels.offsetX,y:e+r.labels.offsetY,text:n(a,i),textAnchor:"middle",fontSize:r.labels.style.fontSize,fontFamily:r.labels.style.fontFamily,foreColor:Array.isArray(r.labels.style.colors)?r.labels.style.colors[i]:r.labels.style.colors})}}]),t}(),St=function(){function t(i){e(this,t),this.ctx=i,this.w=i.w;var a=this.w;this.chartType=this.w.config.chart.type,this.initialAnim=this.w.config.chart.animations.enabled,this.dynamicAnim=this.initialAnim&&this.w.config.chart.animations.dynamicAnimation.enabled,this.animBeginArr=[0],this.animDur=0,this.donutDataLabels=this.w.config.plotOptions.pie.donut.labels,this.lineColorArr=void 0!==a.globals.stroke.colors?a.globals.stroke.colors:a.globals.colors,this.defaultSize=a.globals.svgHeight<a.globals.svgWidth?a.globals.gridHeight:a.globals.gridWidth,this.centerY=this.defaultSize/2,this.centerX=a.globals.gridWidth/2,this.fullAngle=360,a.globals.radialSize=this.defaultSize/2.05-a.config.stroke.width-(a.config.chart.sparkline.enabled?0:a.config.chart.dropShadow.blur),this.donutSize=a.globals.radialSize*parseInt(a.config.plotOptions.pie.donut.size,10)/100,this.maxY=0,this.sliceLabels=[],this.sliceSizes=[],this.prevSectorAngleArr=[]}return a(t,[{key:"draw",value:function(t){var e=this,i=this.w,a=new b(this.ctx);if(this.ret=a.group({class:"apexcharts-pie"}),i.globals.noData)return this.ret;for(var s=0,r=0;r<t.length;r++)s+=f.negToZero(t[r]);var n=[],o=a.group();0===s&&(s=1e-5),t.forEach((function(t){e.maxY=Math.max(e.maxY,t)})),"polarArea"===this.chartType&&this.drawPolarElements();for(var l=0;l<t.length;l++){var h=this.fullAngle*f.negToZero(t[l])/s;n.push(h),"polarArea"===this.chartType?(n[l]=this.fullAngle/t.length,this.sliceSizes.push(i.globals.radialSize*t[l]/this.maxY)):this.sliceSizes.push(i.globals.radialSize)}if(i.globals.dataChanged){for(var c,d=0,g=0;g<i.globals.previousPaths.length;g++)d+=f.negToZero(i.globals.previousPaths[g]);for(var u=0;u<i.globals.previousPaths.length;u++)c=this.fullAngle*f.negToZero(i.globals.previousPaths[u])/d,this.prevSectorAngleArr.push(c)}this.donutSize<0&&(this.donutSize=0);var p=i.config.plotOptions.pie.customScale,x=i.globals.gridWidth/2,m=i.globals.gridHeight/2,v=x-i.globals.gridWidth/2*p,y=m-i.globals.gridHeight/2*p;if("donut"===this.chartType){var w=a.drawCircle(this.donutSize);w.attr({cx:this.centerX,cy:this.centerY,fill:i.config.plotOptions.pie.donut.background?i.config.plotOptions.pie.donut.background:"transparent"}),o.add(w)}var k=this.drawArcs(n,t);if(this.sliceLabels.forEach((function(t){k.add(t)})),o.attr({transform:"translate(".concat(v,", ").concat(y,") scale(").concat(p,")")}),o.add(k),this.ret.add(o),this.donutDataLabels.show){var A=this.renderInnerDataLabels(this.donutDataLabels,{hollowSize:this.donutSize,centerX:this.centerX,centerY:this.centerY,opacity:this.donutDataLabels.show,translateX:v,translateY:y});this.ret.add(A)}return this.ret}},{key:"drawArcs",value:function(t,e){var i=this.w,a=new p(this.ctx),s=new b(this.ctx),r=new T(this.ctx),n=s.group({class:"apexcharts-slices"}),o=i.config.plotOptions.pie.startAngle%this.fullAngle,l=o,h=o,c=o,d=o;this.strokeWidth=i.config.stroke.show?i.config.stroke.width:0;for(var g=0;g<t.length;g++){var u=s.group({class:"apexcharts-series apexcharts-pie-series",seriesName:f.escapeString(i.globals.seriesNames[g]),rel:g+1,"data:realIndex":g});n.add(u),h=d,c=(l=c)+t[g],d=h+this.prevSectorAngleArr[g];var x=c<l?this.fullAngle+c-l:c-l,m=r.fillPath({seriesNumber:g,size:this.sliceSizes[g],value:e[g]}),v=this.getChangedPath(h,d),y=s.drawPath({d:v,stroke:this.lineColorArr instanceof Array?this.lineColorArr[g]:this.lineColorArr,strokeWidth:0,fill:m,fillOpacity:i.config.fill.opacity,classes:"apexcharts-pie-area apexcharts-".concat(this.chartType.toLowerCase(),"-slice-").concat(g)});if(y.attr({index:0,j:g}),i.config.chart.dropShadow.enabled){var w=i.config.chart.dropShadow;a.dropShadow(y,w,g)}this.addListeners(y,this.donutDataLabels),b.setAttrs(y.node,{"data:angle":x,"data:startAngle":l,"data:strokeWidth":this.strokeWidth,"data:value":e[g]});var k={x:0,y:0};"pie"===this.chartType||"polarArea"===this.chartType?k=f.polarToCartesian(this.centerX,this.centerY,i.globals.radialSize/1.25+i.config.plotOptions.pie.dataLabels.offset,(l+x/2)%this.fullAngle):"donut"===this.chartType&&(k=f.polarToCartesian(this.centerX,this.centerY,(i.globals.radialSize+this.donutSize)/2+i.config.plotOptions.pie.dataLabels.offset,(l+x/2)%this.fullAngle)),u.add(y);var A=0;if(!this.initialAnim||i.globals.resized||i.globals.dataChanged?this.animBeginArr.push(0):(0===(A=x/this.fullAngle*i.config.chart.animations.speed)&&(A=1),this.animDur=A+this.animDur,this.animBeginArr.push(this.animDur)),this.dynamicAnim&&i.globals.dataChanged?this.animatePaths(y,{size:this.sliceSizes[g],endAngle:c,startAngle:l,prevStartAngle:h,prevEndAngle:d,animateStartingPos:!0,i:g,animBeginArr:this.animBeginArr,shouldSetPrevPaths:!0,dur:i.config.chart.animations.dynamicAnimation.speed}):this.animatePaths(y,{size:this.sliceSizes[g],endAngle:c,startAngle:l,i:g,totalItems:t.length-1,animBeginArr:this.animBeginArr,dur:A}),i.config.plotOptions.pie.expandOnClick&&"polarArea"!==this.chartType&&y.click(this.pieClicked.bind(this,g)),i.config.dataLabels.enabled){var S=k.x,C=k.y,L=100*x/this.fullAngle+"%";if(0!==x&&i.config.plotOptions.pie.dataLabels.minAngleToShowLabel<t[g]){var P=i.config.dataLabels.formatter;void 0!==P&&(L=P(i.globals.seriesPercent[g][0],{seriesIndex:g,w:i}));var z=i.globals.dataLabels.style.colors[g],I=s.group({class:"apexcharts-datalabels"}),M=s.drawText({x:S,y:C,text:L,textAnchor:"middle",fontSize:i.config.dataLabels.style.fontSize,fontFamily:i.config.dataLabels.style.fontFamily,fontWeight:i.config.dataLabels.style.fontWeight,foreColor:z});if(I.add(M),i.config.dataLabels.dropShadow.enabled){var E=i.config.dataLabels.dropShadow;a.dropShadow(M,E)}M.node.classList.add("apexcharts-pie-label"),i.config.chart.animations.animate&&!1===i.globals.resized&&(M.node.classList.add("apexcharts-pie-label-delay"),M.node.style.animationDelay=i.config.chart.animations.speed/940+"s"),this.sliceLabels.push(I)}}}return n}},{key:"addListeners",value:function(t,e){var i=new b(this.ctx);t.node.addEventListener("mouseenter",i.pathMouseEnter.bind(this,t)),t.node.addEventListener("mouseleave",i.pathMouseLeave.bind(this,t)),t.node.addEventListener("mouseleave",this.revertDataLabelsInner.bind(this,t.node,e)),t.node.addEventListener("mousedown",i.pathMouseDown.bind(this,t)),this.donutDataLabels.total.showAlways||(t.node.addEventListener("mouseenter",this.printDataLabelsInner.bind(this,t.node,e)),t.node.addEventListener("mousedown",this.printDataLabelsInner.bind(this,t.node,e)))}},{key:"animatePaths",value:function(t,e){var i=this.w,a=e.endAngle<e.startAngle?this.fullAngle+e.endAngle-e.startAngle:e.endAngle-e.startAngle,s=a,r=e.startAngle,n=e.startAngle;void 0!==e.prevStartAngle&&void 0!==e.prevEndAngle&&(r=e.prevEndAngle,s=e.prevEndAngle<e.prevStartAngle?this.fullAngle+e.prevEndAngle-e.prevStartAngle:e.prevEndAngle-e.prevStartAngle),e.i===i.config.series.length-1&&(a+n>this.fullAngle?e.endAngle=e.endAngle-(a+n):a+n<this.fullAngle&&(e.endAngle=e.endAngle+(this.fullAngle-(a+n)))),a===this.fullAngle&&(a=this.fullAngle-.01),this.animateArc(t,r,n,a,s,e)}},{key:"animateArc",value:function(t,e,i,a,s,r){var n,o=this,l=this.w,h=new x(this.ctx),c=r.size;(isNaN(e)||isNaN(s))&&(e=i,s=a,r.dur=0);var d=a,g=i,u=e<i?this.fullAngle+e-i:e-i;l.globals.dataChanged&&r.shouldSetPrevPaths&&r.prevEndAngle&&(n=o.getPiePath({me:o,startAngle:r.prevStartAngle,angle:r.prevEndAngle<r.prevStartAngle?this.fullAngle+r.prevEndAngle-r.prevStartAngle:r.prevEndAngle-r.prevStartAngle,size:c}),t.attr({d:n})),0!==r.dur?t.animate(r.dur,l.globals.easing,r.animBeginArr[r.i]).afterAll((function(){"pie"!==o.chartType&&"donut"!==o.chartType&&"polarArea"!==o.chartType||this.animate(l.config.chart.animations.dynamicAnimation.speed).attr({"stroke-width":o.strokeWidth}),r.i===l.config.series.length-1&&h.animationCompleted(t)})).during((function(l){d=u+(a-u)*l,r.animateStartingPos&&(d=s+(a-s)*l,g=e-s+(i-(e-s))*l),n=o.getPiePath({me:o,startAngle:g,angle:d,size:c}),t.node.setAttribute("data:pathOrig",n),t.attr({d:n})})):(n=o.getPiePath({me:o,startAngle:g,angle:a,size:c}),r.isTrack||(l.globals.animationEnded=!0),t.node.setAttribute("data:pathOrig",n),t.attr({d:n,"stroke-width":o.strokeWidth}))}},{key:"pieClicked",value:function(t){var e,i=this.w,a=this.sliceSizes[t]+(i.config.plotOptions.pie.expandOnClick?4:0),s=i.globals.dom.Paper.select(".apexcharts-".concat(this.chartType.toLowerCase(),"-slice-").concat(t)).members[0];if("true"!==s.attr("data:pieClicked")){var r=i.globals.dom.baseEl.getElementsByClassName("apexcharts-pie-area");Array.prototype.forEach.call(r,(function(t){t.setAttribute("data:pieClicked","false");var e=t.getAttribute("data:pathOrig");t.setAttribute("d",e)})),s.attr("data:pieClicked","true");var n=parseInt(s.attr("data:startAngle"),10),o=parseInt(s.attr("data:angle"),10);e=this.getPiePath({me:this,startAngle:n,angle:o,size:a}),360!==o&&s.plot(e)}else{s.attr({"data:pieClicked":"false"}),this.revertDataLabelsInner(s.node,this.donutDataLabels);var l=s.attr("data:pathOrig");s.attr({d:l})}}},{key:"getChangedPath",value:function(t,e){var i="";return this.dynamicAnim&&this.w.globals.dataChanged&&(i=this.getPiePath({me:this,startAngle:t,angle:e-t,size:this.size})),i}},{key:"getPiePath",value:function(t){var e=t.me,i=t.startAngle,a=t.angle,s=t.size,r=i,n=Math.PI*(r-90)/180,o=a+i;Math.ceil(o)>=this.fullAngle+this.w.config.plotOptions.pie.startAngle%this.fullAngle&&(o=this.fullAngle+this.w.config.plotOptions.pie.startAngle%this.fullAngle-.01),Math.ceil(o)>this.fullAngle&&(o-=this.fullAngle);var l=Math.PI*(o-90)/180,h=e.centerX+s*Math.cos(n),c=e.centerY+s*Math.sin(n),d=e.centerX+s*Math.cos(l),g=e.centerY+s*Math.sin(l),u=f.polarToCartesian(e.centerX,e.centerY,e.donutSize,o),p=f.polarToCartesian(e.centerX,e.centerY,e.donutSize,r),x=a>180?1:0,b=["M",h,c,"A",s,s,0,x,1,d,g];return"donut"===e.chartType?[].concat(b,["L",u.x,u.y,"A",e.donutSize,e.donutSize,0,x,0,p.x,p.y,"L",h,c,"z"]).join(" "):"pie"===e.chartType||"polarArea"===e.chartType?[].concat(b,["L",e.centerX,e.centerY,"L",h,c]).join(" "):[].concat(b).join(" ")}},{key:"drawPolarElements",value:function(){var t=this.w,e=new q(this.ctx),i=new b(this.ctx),a=new At(this.ctx),s=i.group(),r=i.group(),n=void 0===t.config.yaxis[0].max&&void 0===t.config.yaxis[0].min,o=e.niceScale(0,Math.ceil(this.maxY),t.config.yaxis[0].tickAmount,0,n),l=o.result.reverse(),h=o.result.length;this.maxY=o.niceMax;for(var c=t.globals.radialSize,d=c/(h-1),g=0;g<h-1;g++){var u=i.drawCircle(c);if(u.attr({cx:this.centerX,cy:this.centerY,fill:"none","stroke-width":t.config.plotOptions.polarArea.rings.strokeWidth,stroke:t.config.plotOptions.polarArea.rings.strokeColor}),t.config.yaxis[0].show){var f=a.drawYAxisTexts(this.centerX,this.centerY-c+parseInt(t.config.yaxis[0].labels.style.fontSize,10)/2,g,l[g]);r.add(f)}s.add(u),c-=d}this.ret.add(s),this.ret.add(r)}},{key:"renderInnerDataLabels",value:function(t,e){var i=this.w,a=new b(this.ctx),s=a.group({class:"apexcharts-datalabels-group",transform:"translate(".concat(e.translateX?e.translateX:0,", ").concat(e.translateY?e.translateY:0,") scale(").concat(i.config.plotOptions.pie.customScale,")")}),r=t.total.show;s.node.style.opacity=e.opacity;var n,o,l=e.centerX,h=e.centerY;n=void 0===t.name.color?i.globals.colors[0]:t.name.color;var c=t.name.fontSize,d=t.name.fontFamily,g=t.value.fontWeight;o=void 0===t.value.color?i.config.chart.foreColor:t.value.color;var u=t.value.formatter,f="",p="";if(r?(n=t.total.color,c=t.total.fontSize,d=t.total.fontFamily,g=t.total.fontWeight,p=t.total.label,f=t.total.formatter(i)):1===i.globals.series.length&&(f=u(i.globals.series[0],i),p=i.globals.seriesNames[0]),p&&(p=t.name.formatter(p,t.total.show,i)),t.name.show){var x=a.drawText({x:l,y:h+parseFloat(t.name.offsetY),text:p,textAnchor:"middle",foreColor:n,fontSize:c,fontWeight:g,fontFamily:d});x.node.classList.add("apexcharts-datalabel-label"),s.add(x)}if(t.value.show){var m=t.name.show?parseFloat(t.value.offsetY)+16:t.value.offsetY,v=a.drawText({x:l,y:h+m,text:f,textAnchor:"middle",foreColor:o,fontWeight:t.value.fontWeight,fontSize:t.value.fontSize,fontFamily:t.value.fontFamily});v.node.classList.add("apexcharts-datalabel-value"),s.add(v)}return s}},{key:"printInnerLabels",value:function(t,e,i,a){var s,r=this.w;a?s=void 0===t.name.color?r.globals.colors[parseInt(a.parentNode.getAttribute("rel"),10)-1]:t.name.color:r.globals.series.length>1&&t.total.show&&(s=t.total.color);var n=r.globals.dom.baseEl.querySelector(".apexcharts-datalabel-label"),o=r.globals.dom.baseEl.querySelector(".apexcharts-datalabel-value");i=(0,t.value.formatter)(i,r),a||"function"!=typeof t.total.formatter||(i=t.total.formatter(r));var l=e===t.total.label;e=t.name.formatter(e,l,r),null!==n&&(n.textContent=e),null!==o&&(o.textContent=i),null!==n&&(n.style.fill=s)}},{key:"printDataLabelsInner",value:function(t,e){var i=this.w,a=t.getAttribute("data:value"),s=i.globals.seriesNames[parseInt(t.parentNode.getAttribute("rel"),10)-1];i.globals.series.length>1&&this.printInnerLabels(e,s,a,t);var r=i.globals.dom.baseEl.querySelector(".apexcharts-datalabels-group");null!==r&&(r.style.opacity=1)}},{key:"revertDataLabelsInner",value:function(t,e,i){var a=this,s=this.w,r=s.globals.dom.baseEl.querySelector(".apexcharts-datalabels-group"),n=!1,o=s.globals.dom.baseEl.getElementsByClassName("apexcharts-pie-area"),l=function(t){var i=t.makeSliceOut,s=t.printLabel;Array.prototype.forEach.call(o,(function(t){"true"===t.getAttribute("data:pieClicked")&&(i&&(n=!0),s&&a.printDataLabelsInner(t,e))}))};if(l({makeSliceOut:!0,printLabel:!1}),e.total.show&&s.globals.series.length>1)n&&!e.total.showAlways?l({makeSliceOut:!1,printLabel:!0}):this.printInnerLabels(e,e.total.label,e.total.formatter(s));else if(l({makeSliceOut:!1,printLabel:!0}),!n)if(s.globals.selectedDataPoints.length&&s.globals.series.length>1)if(s.globals.selectedDataPoints[0].length>0){var h=s.globals.selectedDataPoints[0],c=s.globals.dom.baseEl.querySelector(".apexcharts-".concat(this.chartType.toLowerCase(),"-slice-").concat(h));this.printDataLabelsInner(c,e)}else r&&s.globals.selectedDataPoints.length&&0===s.globals.selectedDataPoints[0].length&&(r.style.opacity=0);else r&&s.globals.series.length>1&&(r.style.opacity=0)}}]),t}(),Ct=function(){function t(i){e(this,t),this.ctx=i,this.w=i.w,this.chartType=this.w.config.chart.type,this.initialAnim=this.w.config.chart.animations.enabled,this.dynamicAnim=this.initialAnim&&this.w.config.chart.animations.dynamicAnimation.enabled,this.animDur=0;var a=this.w;this.graphics=new b(this.ctx),this.lineColorArr=void 0!==a.globals.stroke.colors?a.globals.stroke.colors:a.globals.colors,this.defaultSize=a.globals.svgHeight<a.globals.svgWidth?a.globals.gridHeight+1.5*a.globals.goldenPadding:a.globals.gridWidth,this.maxValue=this.w.globals.maxY,this.minValue=this.w.globals.minY,this.polygons=a.config.plotOptions.radar.polygons,this.strokeWidth=a.config.stroke.show?a.config.stroke.width:0,this.size=this.defaultSize/2.1-this.strokeWidth-a.config.chart.dropShadow.blur,a.config.xaxis.labels.show&&(this.size=this.size-a.globals.xAxisLabelsWidth/1.75),void 0!==a.config.plotOptions.radar.size&&(this.size=a.config.plotOptions.radar.size),this.dataRadiusOfPercent=[],this.dataRadius=[],this.angleArr=[],this.yaxisLabelsTextsPos=[]}return a(t,[{key:"draw",value:function(t){var e=this,i=this.w,a=new T(this.ctx),s=[],r=new M(this.ctx);t.length&&(this.dataPointsLen=t[i.globals.maxValsInArrayIndex].length),this.disAngle=2*Math.PI/this.dataPointsLen;var o=i.globals.gridWidth/2,l=i.globals.gridHeight/2,h=o+i.config.plotOptions.radar.offsetX,c=l+i.config.plotOptions.radar.offsetY,d=this.graphics.group({class:"apexcharts-radar-series apexcharts-plot-series",transform:"translate(".concat(h||0,", ").concat(c||0,")")}),g=[],u=null,x=null;if(this.yaxisLabels=this.graphics.group({class:"apexcharts-yaxis"}),t.forEach((function(t,o){var l=t.length===i.globals.dataPoints,h=e.graphics.group().attr({class:"apexcharts-series","data:longestSeries":l,seriesName:f.escapeString(i.globals.seriesNames[o]),rel:o+1,"data:realIndex":o});e.dataRadiusOfPercent[o]=[],e.dataRadius[o]=[],e.angleArr[o]=[],t.forEach((function(t,i){var a=Math.abs(e.maxValue-e.minValue);t+=Math.abs(e.minValue),e.dataRadiusOfPercent[o][i]=t/a,e.dataRadius[o][i]=e.dataRadiusOfPercent[o][i]*e.size,e.angleArr[o][i]=i*e.disAngle})),g=e.getDataPointsPos(e.dataRadius[o],e.angleArr[o]);var c=e.createPaths(g,{x:0,y:0});u=e.graphics.group({class:"apexcharts-series-markers-wrap apexcharts-element-hidden"}),x=e.graphics.group({class:"apexcharts-datalabels","data:realIndex":o}),i.globals.delayedElements.push({el:u.node,index:o});var d={i:o,realIndex:o,animationDelay:o,initialSpeed:i.config.chart.animations.speed,dataChangeSpeed:i.config.chart.animations.dynamicAnimation.speed,className:"apexcharts-radar",shouldClipToGrid:!1,bindEventsOnPaths:!1,stroke:i.globals.stroke.colors[o],strokeLineCap:i.config.stroke.lineCap},b=null;i.globals.previousPaths.length>0&&(b=e.getPreviousPath(o));for(var m=0;m<c.linePathsTo.length;m++){var v=e.graphics.renderPaths(n(n({},d),{},{pathFrom:null===b?c.linePathsFrom[m]:b,pathTo:c.linePathsTo[m],strokeWidth:Array.isArray(e.strokeWidth)?e.strokeWidth[o]:e.strokeWidth,fill:"none",drawShadow:!1}));h.add(v);var y=a.fillPath({seriesNumber:o}),w=e.graphics.renderPaths(n(n({},d),{},{pathFrom:null===b?c.areaPathsFrom[m]:b,pathTo:c.areaPathsTo[m],strokeWidth:0,fill:y,drawShadow:!1}));if(i.config.chart.dropShadow.enabled){var k=new p(e.ctx),A=i.config.chart.dropShadow;k.dropShadow(w,Object.assign({},A,{noUserSpaceOnUse:!0}),o)}h.add(w)}t.forEach((function(t,a){var s=new z(e.ctx).getMarkerConfig("apexcharts-marker",o,a),l=e.graphics.drawMarker(g[a].x,g[a].y,s);l.attr("rel",a),l.attr("j",a),l.attr("index",o),l.node.setAttribute("default-marker-size",s.pSize);var c=e.graphics.group({class:"apexcharts-series-markers"});c&&c.add(l),u.add(c),h.add(u);var d=i.config.dataLabels;if(d.enabled){var f=d.formatter(i.globals.series[o][a],{seriesIndex:o,dataPointIndex:a,w:i});r.plotDataLabelsText({x:g[a].x,y:g[a].y,text:f,textAnchor:"middle",i:o,j:o,parent:x,offsetCorrection:!1,dataLabelsConfig:n({},d)})}h.add(x)})),s.push(h)})),this.drawPolygons({parent:d}),i.config.xaxis.labels.show){var b=this.drawXAxisTexts();d.add(b)}return d.add(this.yaxisLabels),s.forEach((function(t){d.add(t)})),d}},{key:"drawPolygons",value:function(t){for(var e=this,i=this.w,a=t.parent,s=new At(this.ctx),r=i.globals.yAxisScale[0].result.reverse(),n=r.length,o=[],l=this.size/(n-1),h=0;h<n;h++)o[h]=l*h;o.reverse();var c=[],d=[];o.forEach((function(t,i){var a=e.getPolygonPos(t),s="";a.forEach((function(t,a){if(0===i){var r=e.graphics.drawLine(t.x,t.y,0,0,Array.isArray(e.polygons.connectorColors)?e.polygons.connectorColors[a]:e.polygons.connectorColors);d.push(r)}0===a&&e.yaxisLabelsTextsPos.push({x:t.x,y:t.y}),s+=t.x+","+t.y+" "})),c.push(s)})),c.forEach((function(t,s){var r=e.polygons.strokeColors,n=e.polygons.strokeWidth,o=e.graphics.drawPolygon(t,Array.isArray(r)?r[s]:r,Array.isArray(n)?n[s]:n,i.globals.radarPolygons.fill.colors[s]);a.add(o)})),d.forEach((function(t){a.add(t)})),i.config.yaxis[0].show&&this.yaxisLabelsTextsPos.forEach((function(t,i){var a=s.drawYAxisTexts(t.x,t.y,i,r[i]);e.yaxisLabels.add(a)}))}},{key:"drawXAxisTexts",value:function(){var t=this,e=this.w,i=e.config.xaxis.labels,a=this.graphics.group({class:"apexcharts-xaxis"}),s=this.getPolygonPos(this.size);return e.globals.labels.forEach((function(r,o){var l=e.config.xaxis.labels.formatter,h=new M(t.ctx);if(s[o]){var c=t.getTextPos(s[o],t.size),d=l(r,{seriesIndex:-1,dataPointIndex:o,w:e});h.plotDataLabelsText({x:c.newX,y:c.newY,text:d,textAnchor:c.textAnchor,i:o,j:o,parent:a,color:i.style.colors[o]?i.style.colors[o]:"#a8a8a8",dataLabelsConfig:n({textAnchor:c.textAnchor,dropShadow:{enabled:!1}},i),offsetCorrection:!1})}})),a}},{key:"createPaths",value:function(t,e){var i=this,a=[],s=[],r=[],n=[];if(t.length){s=[this.graphics.move(e.x,e.y)],n=[this.graphics.move(e.x,e.y)];var o=this.graphics.move(t[0].x,t[0].y),l=this.graphics.move(t[0].x,t[0].y);t.forEach((function(e,a){o+=i.graphics.line(e.x,e.y),l+=i.graphics.line(e.x,e.y),a===t.length-1&&(o+="Z",l+="Z")})),a.push(o),r.push(l)}return{linePathsFrom:s,linePathsTo:a,areaPathsFrom:n,areaPathsTo:r}}},{key:"getTextPos",value:function(t,e){var i="middle",a=t.x,s=t.y;return Math.abs(t.x)>=10?t.x>0?(i="start",a+=10):t.x<0&&(i="end",a-=10):i="middle",Math.abs(t.y)>=e-10&&(t.y<0?s-=10:t.y>0&&(s+=10)),{textAnchor:i,newX:a,newY:s}}},{key:"getPreviousPath",value:function(t){for(var e=this.w,i=null,a=0;a<e.globals.previousPaths.length;a++){var s=e.globals.previousPaths[a];s.paths.length>0&&parseInt(s.realIndex,10)===parseInt(t,10)&&void 0!==e.globals.previousPaths[a].paths[0]&&(i=e.globals.previousPaths[a].paths[0].d)}return i}},{key:"getDataPointsPos",value:function(t,e){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:this.dataPointsLen;t=t||[],e=e||[];for(var a=[],s=0;s<i;s++){var r={};r.x=t[s]*Math.sin(e[s]),r.y=-t[s]*Math.cos(e[s]),a.push(r)}return a}},{key:"getPolygonPos",value:function(t){for(var e=[],i=2*Math.PI/this.dataPointsLen,a=0;a<this.dataPointsLen;a++){var s={};s.x=t*Math.sin(a*i),s.y=-t*Math.cos(a*i),e.push(s)}return e}}]),t}(),Lt=function(t){o(s,t);var i=d(s);function s(t){var a;e(this,s),(a=i.call(this,t)).ctx=t,a.w=t.w,a.animBeginArr=[0],a.animDur=0;var r=a.w;return a.startAngle=r.config.plotOptions.radialBar.startAngle,a.endAngle=r.config.plotOptions.radialBar.endAngle,a.totalAngle=Math.abs(r.config.plotOptions.radialBar.endAngle-r.config.plotOptions.radialBar.startAngle),a.trackStartAngle=r.config.plotOptions.radialBar.track.startAngle,a.trackEndAngle=r.config.plotOptions.radialBar.track.endAngle,a.radialDataLabels=r.config.plotOptions.radialBar.dataLabels,a.trackStartAngle||(a.trackStartAngle=a.startAngle),a.trackEndAngle||(a.trackEndAngle=a.endAngle),360===a.endAngle&&(a.endAngle=359.99),a.margin=parseInt(r.config.plotOptions.radialBar.track.margin,10),a}return a(s,[{key:"draw",value:function(t){var e=this.w,i=new b(this.ctx),a=i.group({class:"apexcharts-radialbar"});if(e.globals.noData)return a;var s=i.group(),r=this.defaultSize/2,n=e.globals.gridWidth/2,o=this.defaultSize/2.05;e.config.chart.sparkline.enabled||(o=o-e.config.stroke.width-e.config.chart.dropShadow.blur);var l=e.globals.fill.colors;if(e.config.plotOptions.radialBar.track.show){var h=this.drawTracks({size:o,centerX:n,centerY:r,colorArr:l,series:t});s.add(h)}var c=this.drawArcs({size:o,centerX:n,centerY:r,colorArr:l,series:t}),d=360;e.config.plotOptions.radialBar.startAngle<0&&(d=this.totalAngle);var g=(360-d)/360;if(e.globals.radialSize=o-o*g,this.radialDataLabels.value.show){var u=Math.max(this.radialDataLabels.value.offsetY,this.radialDataLabels.name.offsetY);e.globals.radialSize+=u*g}return s.add(c.g),"front"===e.config.plotOptions.radialBar.hollow.position&&(c.g.add(c.elHollow),c.dataLabels&&c.g.add(c.dataLabels)),a.add(s),a}},{key:"drawTracks",value:function(t){var e=this.w,i=new b(this.ctx),a=i.group({class:"apexcharts-tracks"}),s=new p(this.ctx),r=new T(this.ctx),n=this.getStrokeWidth(t);t.size=t.size-n/2;for(var o=0;o<t.series.length;o++){var l=i.group({class:"apexcharts-radialbar-track apexcharts-track"});a.add(l),l.attr({rel:o+1}),t.size=t.size-n-this.margin;var h=e.config.plotOptions.radialBar.track,c=r.fillPath({seriesNumber:0,size:t.size,fillColors:Array.isArray(h.background)?h.background[o]:h.background,solid:!0}),d=this.trackStartAngle,g=this.trackEndAngle;Math.abs(g)+Math.abs(d)>=360&&(g=360-Math.abs(this.startAngle)-.1);var u=i.drawPath({d:"",stroke:c,strokeWidth:n*parseInt(h.strokeWidth,10)/100,fill:"none",strokeOpacity:h.opacity,classes:"apexcharts-radialbar-area"});if(h.dropShadow.enabled){var f=h.dropShadow;s.dropShadow(u,f)}l.add(u),u.attr("id","apexcharts-radialbarTrack-"+o),this.animatePaths(u,{centerX:t.centerX,centerY:t.centerY,endAngle:g,startAngle:d,size:t.size,i:o,totalItems:2,animBeginArr:0,dur:0,isTrack:!0,easing:e.globals.easing})}return a}},{key:"drawArcs",value:function(t){var e=this.w,i=new b(this.ctx),a=new T(this.ctx),s=new p(this.ctx),r=i.group(),n=this.getStrokeWidth(t);t.size=t.size-n/2;var o=e.config.plotOptions.radialBar.hollow.background,l=t.size-n*t.series.length-this.margin*t.series.length-n*parseInt(e.config.plotOptions.radialBar.track.strokeWidth,10)/100/2,h=l-e.config.plotOptions.radialBar.hollow.margin;void 0!==e.config.plotOptions.radialBar.hollow.image&&(o=this.drawHollowImage(t,r,l,o));var c=this.drawHollow({size:h,centerX:t.centerX,centerY:t.centerY,fill:o||"transparent"});if(e.config.plotOptions.radialBar.hollow.dropShadow.enabled){var d=e.config.plotOptions.radialBar.hollow.dropShadow;s.dropShadow(c,d)}var g=1;!this.radialDataLabels.total.show&&e.globals.series.length>1&&(g=0);var u=null;this.radialDataLabels.show&&(u=this.renderInnerDataLabels(this.radialDataLabels,{hollowSize:l,centerX:t.centerX,centerY:t.centerY,opacity:g})),"back"===e.config.plotOptions.radialBar.hollow.position&&(r.add(c),u&&r.add(u));var x=!1;e.config.plotOptions.radialBar.inverseOrder&&(x=!0);for(var m=x?t.series.length-1:0;x?m>=0:m<t.series.length;x?m--:m++){var v=i.group({class:"apexcharts-series apexcharts-radial-series",seriesName:f.escapeString(e.globals.seriesNames[m])});r.add(v),v.attr({rel:m+1,"data:realIndex":m}),this.ctx.series.addCollapsedClassToSeries(v,m),t.size=t.size-n-this.margin;var y=a.fillPath({seriesNumber:m,size:t.size,value:t.series[m]}),w=this.startAngle,k=void 0,A=f.negToZero(t.series[m]>100?100:t.series[m])/100,S=Math.round(this.totalAngle*A)+this.startAngle,C=void 0;e.globals.dataChanged&&(k=this.startAngle,C=Math.round(this.totalAngle*f.negToZero(e.globals.previousPaths[m])/100)+k),Math.abs(S)+Math.abs(w)>=360&&(S-=.01),Math.abs(C)+Math.abs(k)>=360&&(C-=.01);var L=S-w,P=Array.isArray(e.config.stroke.dashArray)?e.config.stroke.dashArray[m]:e.config.stroke.dashArray,z=i.drawPath({d:"",stroke:y,strokeWidth:n,fill:"none",fillOpacity:e.config.fill.opacity,classes:"apexcharts-radialbar-area apexcharts-radialbar-slice-"+m,strokeDashArray:P});if(b.setAttrs(z.node,{"data:angle":L,"data:value":t.series[m]}),e.config.chart.dropShadow.enabled){var I=e.config.chart.dropShadow;s.dropShadow(z,I,m)}this.addListeners(z,this.radialDataLabels),v.add(z),z.attr({index:0,j:m});var M=0;!this.initialAnim||e.globals.resized||e.globals.dataChanged||(M=(S-w)/360*e.config.chart.animations.speed,this.animDur=M/(1.2*t.series.length)+this.animDur,this.animBeginArr.push(this.animDur)),e.globals.dataChanged&&(M=(S-w)/360*e.config.chart.animations.dynamicAnimation.speed,this.animDur=M/(1.2*t.series.length)+this.animDur,this.animBeginArr.push(this.animDur)),this.animatePaths(z,{centerX:t.centerX,centerY:t.centerY,endAngle:S,startAngle:w,prevEndAngle:C,prevStartAngle:k,size:t.size,i:m,totalItems:2,animBeginArr:this.animBeginArr,dur:M,shouldSetPrevPaths:!0,easing:e.globals.easing})}return{g:r,elHollow:c,dataLabels:u}}},{key:"drawHollow",value:function(t){var e=new b(this.ctx).drawCircle(2*t.size);return e.attr({class:"apexcharts-radialbar-hollow",cx:t.centerX,cy:t.centerY,r:t.size,fill:t.fill}),e}},{key:"drawHollowImage",value:function(t,e,i,a){var s=this.w,r=new T(this.ctx),n=f.randomId(),o=s.config.plotOptions.radialBar.hollow.image;if(s.config.plotOptions.radialBar.hollow.imageClipped)r.clippedImgArea({width:i,height:i,image:o,patternID:"pattern".concat(s.globals.cuid).concat(n)}),a="url(#pattern".concat(s.globals.cuid).concat(n,")");else{var l=s.config.plotOptions.radialBar.hollow.imageWidth,h=s.config.plotOptions.radialBar.hollow.imageHeight;if(void 0===l&&void 0===h){var c=s.globals.dom.Paper.image(o).loaded((function(e){this.move(t.centerX-e.width/2+s.config.plotOptions.radialBar.hollow.imageOffsetX,t.centerY-e.height/2+s.config.plotOptions.radialBar.hollow.imageOffsetY)}));e.add(c)}else{var d=s.globals.dom.Paper.image(o).loaded((function(e){this.move(t.centerX-l/2+s.config.plotOptions.radialBar.hollow.imageOffsetX,t.centerY-h/2+s.config.plotOptions.radialBar.hollow.imageOffsetY),this.size(l,h)}));e.add(d)}}return a}},{key:"getStrokeWidth",value:function(t){var e=this.w;return t.size*(100-parseInt(e.config.plotOptions.radialBar.hollow.size,10))/100/(t.series.length+1)-this.margin}}]),s}(St),Pt=function(){function t(i){e(this,t),this.w=i.w,this.lineCtx=i}return a(t,[{key:"sameValueSeriesFix",value:function(t,e){var i=this.w;if("line"===i.config.chart.type&&("gradient"===i.config.fill.type||"gradient"===i.config.fill.type[t])&&new y(this.lineCtx.ctx,i).seriesHaveSameValues(t)){var a=e[t].slice();a[a.length-1]=a[a.length-1]+1e-6,e[t]=a}return e}},{key:"calculatePoints",value:function(t){var e=t.series,i=t.realIndex,a=t.x,s=t.y,r=t.i,n=t.j,o=t.prevY,l=this.w,h=[],c=[];if(0===n){var d=this.lineCtx.categoryAxisCorrection+l.config.markers.offsetX;l.globals.isXNumeric&&(d=(l.globals.seriesX[i][0]-l.globals.minX)/this.lineCtx.xRatio+l.config.markers.offsetX),h.push(d),c.push(f.isNumber(e[r][0])?o+l.config.markers.offsetY:null),h.push(a+l.config.markers.offsetX),c.push(f.isNumber(e[r][n+1])?s+l.config.markers.offsetY:null)}else h.push(a+l.config.markers.offsetX),c.push(f.isNumber(e[r][n+1])?s+l.config.markers.offsetY:null);return{x:h,y:c}}},{key:"checkPreviousPaths",value:function(t){for(var e=t.pathFromLine,i=t.pathFromArea,a=t.realIndex,s=this.w,r=0;r<s.globals.previousPaths.length;r++){var n=s.globals.previousPaths[r];("line"===n.type||"area"===n.type)&&n.paths.length>0&&parseInt(n.realIndex,10)===parseInt(a,10)&&("line"===n.type?(this.lineCtx.appendPathFrom=!1,e=s.globals.previousPaths[r].paths[0].d):"area"===n.type&&(this.lineCtx.appendPathFrom=!1,i=s.globals.previousPaths[r].paths[0].d,s.config.stroke.show&&s.globals.previousPaths[r].paths[1]&&(e=s.globals.previousPaths[r].paths[1].d)))}return{pathFromLine:e,pathFromArea:i}}},{key:"determineFirstPrevY",value:function(t){var e=t.i,i=t.series,a=t.prevY,s=t.lineYPosition,r=this.w;if(void 0!==i[e][0])a=(s=r.config.chart.stacked&&e>0?this.lineCtx.prevSeriesY[e-1][0]:this.lineCtx.zeroY)-i[e][0]/this.lineCtx.yRatio[this.lineCtx.yaxisIndex]+2*(this.lineCtx.isReversed?i[e][0]/this.lineCtx.yRatio[this.lineCtx.yaxisIndex]:0);else if(r.config.chart.stacked&&e>0&&void 0===i[e][0])for(var n=e-1;n>=0;n--)if(null!==i[n][0]&&void 0!==i[n][0]){a=s=this.lineCtx.prevSeriesY[n][0];break}return{prevY:a,lineYPosition:s}}}]),t}(),Tt=function(){function t(i,a,s){e(this,t),this.ctx=i,this.w=i.w,this.xyRatios=a,this.pointsChart=!("bubble"!==this.w.config.chart.type&&"scatter"!==this.w.config.chart.type)||s,this.scatter=new I(this.ctx),this.noNegatives=this.w.globals.minX===Number.MAX_VALUE,this.lineHelpers=new Pt(this),this.markers=new z(this.ctx),this.prevSeriesY=[],this.categoryAxisCorrection=0,this.yaxisIndex=0}return a(t,[{key:"draw",value:function(t,e,i){var a=this.w,s=new b(this.ctx),r=a.globals.comboCharts?e:a.config.chart.type,n=s.group({class:"apexcharts-".concat(r,"-series apexcharts-plot-series")}),o=new y(this.ctx,a);this.yRatio=this.xyRatios.yRatio,this.zRatio=this.xyRatios.zRatio,this.xRatio=this.xyRatios.xRatio,this.baseLineY=this.xyRatios.baseLineY,t=o.getLogSeries(t),this.yRatio=o.getLogYRatios(this.yRatio);for(var l=[],h=0;h<t.length;h++){t=this.lineHelpers.sameValueSeriesFix(h,t);var c=a.globals.comboCharts?i[h]:h;this._initSerieVariables(t,h,c);var d=[],g=[],u=a.globals.padHorizontal+this.categoryAxisCorrection;this.ctx.series.addCollapsedClassToSeries(this.elSeries,c),a.globals.isXNumeric&&a.globals.seriesX.length>0&&(u=(a.globals.seriesX[c][0]-a.globals.minX)/this.xRatio),g.push(u);var f,p=u,x=p,m=this.zeroY;m=this.lineHelpers.determineFirstPrevY({i:h,series:t,prevY:m,lineYPosition:0}).prevY,d.push(m),f=m;var v=this._calculatePathsFrom({series:t,i:h,realIndex:c,prevX:x,prevY:m}),w=this._iterateOverDataPoints({series:t,realIndex:c,i:h,x:u,y:1,pX:p,pY:f,pathsFrom:v,linePaths:[],areaPaths:[],seriesIndex:i,lineYPosition:0,xArrj:g,yArrj:d});this._handlePaths({type:r,realIndex:c,i:h,paths:w}),this.elSeries.add(this.elPointsMain),this.elSeries.add(this.elDataLabelsWrap),l.push(this.elSeries)}for(var k=l.length;k>0;k--)n.add(l[k-1]);return n}},{key:"_initSerieVariables",value:function(t,e,i){var a=this.w,s=new b(this.ctx);this.xDivision=a.globals.gridWidth/(a.globals.dataPoints-("on"===a.config.xaxis.tickPlacement?1:0)),this.strokeWidth=Array.isArray(a.config.stroke.width)?a.config.stroke.width[i]:a.config.stroke.width,this.yRatio.length>1&&(this.yaxisIndex=i),this.isReversed=a.config.yaxis[this.yaxisIndex]&&a.config.yaxis[this.yaxisIndex].reversed,this.zeroY=a.globals.gridHeight-this.baseLineY[this.yaxisIndex]-(this.isReversed?a.globals.gridHeight:0)+(this.isReversed?2*this.baseLineY[this.yaxisIndex]:0),this.areaBottomY=this.zeroY,(this.zeroY>a.globals.gridHeight||"end"===a.config.plotOptions.area.fillTo)&&(this.areaBottomY=a.globals.gridHeight),this.categoryAxisCorrection=this.xDivision/2,this.elSeries=s.group({class:"apexcharts-series",seriesName:f.escapeString(a.globals.seriesNames[i])}),this.elPointsMain=s.group({class:"apexcharts-series-markers-wrap","data:realIndex":i}),this.elDataLabelsWrap=s.group({class:"apexcharts-datalabels","data:realIndex":i});var r=t[e].length===a.globals.dataPoints;this.elSeries.attr({"data:longestSeries":r,rel:e+1,"data:realIndex":i}),this.appendPathFrom=!0}},{key:"_calculatePathsFrom",value:function(t){var e,i,a,s,r=t.series,n=t.i,o=t.realIndex,l=t.prevX,h=t.prevY,c=this.w,d=new b(this.ctx);if(null===r[n][0]){for(var g=0;g<r[n].length;g++)if(null!==r[n][g]){l=this.xDivision*g,h=this.zeroY-r[n][g]/this.yRatio[this.yaxisIndex],e=d.move(l,h),i=d.move(l,this.areaBottomY);break}}else e=d.move(l,h),i=d.move(l,this.areaBottomY)+d.line(l,h);if(a=d.move(-1,this.zeroY)+d.line(-1,this.zeroY),s=d.move(-1,this.zeroY)+d.line(-1,this.zeroY),c.globals.previousPaths.length>0){var u=this.lineHelpers.checkPreviousPaths({pathFromLine:a,pathFromArea:s,realIndex:o});a=u.pathFromLine,s=u.pathFromArea}return{prevX:l,prevY:h,linePath:e,areaPath:i,pathFromLine:a,pathFromArea:s}}},{key:"_handlePaths",value:function(t){var e=t.type,i=t.realIndex,a=t.i,s=t.paths,r=this.w,o=new b(this.ctx),l=new T(this.ctx);this.prevSeriesY.push(s.yArrj),r.globals.seriesXvalues[i]=s.xArrj,r.globals.seriesYvalues[i]=s.yArrj,this.pointsChart||r.globals.delayedElements.push({el:this.elPointsMain.node,index:i});var h={i:a,realIndex:i,animationDelay:a,initialSpeed:r.config.chart.animations.speed,dataChangeSpeed:r.config.chart.animations.dynamicAnimation.speed,className:"apexcharts-".concat(e)};if("area"===e)for(var c=l.fillPath({seriesNumber:i}),d=0;d<s.areaPaths.length;d++){var g=o.renderPaths(n(n({},h),{},{pathFrom:s.pathFromArea,pathTo:s.areaPaths[d],stroke:"none",strokeWidth:0,strokeLineCap:null,fill:c}));this.elSeries.add(g)}if(r.config.stroke.show&&!this.pointsChart){var u=null;u="line"===e?l.fillPath({seriesNumber:i,i:a}):r.globals.stroke.colors[i];for(var f=0;f<s.linePaths.length;f++){var p=o.renderPaths(n(n({},h),{},{pathFrom:s.pathFromLine,pathTo:s.linePaths[f],stroke:u,strokeWidth:this.strokeWidth,strokeLineCap:r.config.stroke.lineCap,fill:"none"}));this.elSeries.add(p)}}}},{key:"_iterateOverDataPoints",value:function(t){for(var e=t.series,i=t.realIndex,a=t.i,s=t.x,r=t.y,n=t.pX,o=t.pY,l=t.pathsFrom,h=t.linePaths,c=t.areaPaths,d=t.seriesIndex,g=t.lineYPosition,u=t.xArrj,p=t.yArrj,x=this.w,m=new b(this.ctx),v=this.yRatio,y=l.prevY,w=l.linePath,k=l.areaPath,A=l.pathFromLine,S=l.pathFromArea,C=f.isNumber(x.globals.minYArr[i])?x.globals.minYArr[i]:x.globals.minY,L=x.globals.dataPoints>1?x.globals.dataPoints-1:x.globals.dataPoints,P=0;P<L;P++){var T=void 0===e[a][P+1]||null===e[a][P+1];if(x.globals.isXNumeric){var z=x.globals.seriesX[i][P+1];void 0===x.globals.seriesX[i][P+1]&&(z=x.globals.seriesX[i][L-1]),s=(z-x.globals.minX)/this.xRatio}else s+=this.xDivision;if(x.config.chart.stacked)if(a>0&&x.globals.collapsedSeries.length<x.config.series.length-1){g=this.prevSeriesY[function(t){for(var e=t,i=0;i<x.globals.series.length;i++)if(x.globals.collapsedSeriesIndices.indexOf(t)>-1){e--;break}return e>=0?e:0}(a-1)][P+1]}else g=this.zeroY;else g=this.zeroY;r=T?g-C/v[this.yaxisIndex]+2*(this.isReversed?C/v[this.yaxisIndex]:0):g-e[a][P+1]/v[this.yaxisIndex]+2*(this.isReversed?e[a][P+1]/v[this.yaxisIndex]:0),u.push(s),p.push(r);var I=this.lineHelpers.calculatePoints({series:e,x:s,y:r,realIndex:i,i:a,j:P,prevY:y}),M=this._createPaths({series:e,i:a,realIndex:i,j:P,x:s,y:r,pX:n,pY:o,linePath:w,areaPath:k,linePaths:h,areaPaths:c,seriesIndex:d});c=M.areaPaths,h=M.linePaths,n=M.pX,o=M.pY,k=M.areaPath,w=M.linePath,this.appendPathFrom&&(A+=m.line(s,this.zeroY),S+=m.line(s,this.zeroY)),this.handleNullDataPoints(e,I,a,P,i),this._handleMarkersAndLabels({pointsPos:I,series:e,x:s,y:r,prevY:y,i:a,j:P,realIndex:i})}return{yArrj:p,xArrj:u,pathFromArea:S,areaPaths:c,pathFromLine:A,linePaths:h}}},{key:"_handleMarkersAndLabels",value:function(t){var e=t.pointsPos,i=(t.series,t.x,t.y,t.prevY,t.i),a=t.j,s=t.realIndex,r=this.w,n=new M(this.ctx);if(this.pointsChart)this.scatter.draw(this.elSeries,a,{realIndex:s,pointsPos:e,zRatio:this.zRatio,elParent:this.elPointsMain});else{r.globals.series[i].length>1&&this.elPointsMain.node.classList.add("apexcharts-element-hidden");var o=this.markers.plotChartMarkers(e,s,a+1);null!==o&&this.elPointsMain.add(o)}var l=n.drawDataLabel(e,s,a+1,null);null!==l&&this.elDataLabelsWrap.add(l)}},{key:"_createPaths",value:function(t){var e=t.series,i=t.i,a=t.realIndex,s=t.j,r=t.x,n=t.y,o=t.pX,l=t.pY,h=t.linePath,c=t.areaPath,d=t.linePaths,g=t.areaPaths,u=t.seriesIndex,f=this.w,p=new b(this.ctx),x=f.config.stroke.curve,m=this.areaBottomY;if(Array.isArray(f.config.stroke.curve)&&(x=Array.isArray(u)?f.config.stroke.curve[u[i]]:f.config.stroke.curve[i]),"smooth"===x){var v=.35*(r-o);f.globals.hasNullValues?(null!==e[i][s]&&(null!==e[i][s+1]?(h=p.move(o,l)+p.curve(o+v,l,r-v,n,r+1,n),c=p.move(o+1,l)+p.curve(o+v,l,r-v,n,r+1,n)+p.line(r,m)+p.line(o,m)+"z"):(h=p.move(o,l),c=p.move(o,l)+"z")),d.push(h),g.push(c)):(h+=p.curve(o+v,l,r-v,n,r,n),c+=p.curve(o+v,l,r-v,n,r,n)),o=r,l=n,s===e[i].length-2&&(c=c+p.curve(o,l,r,n,r,m)+p.move(r,n)+"z",f.globals.hasNullValues||(d.push(h),g.push(c)))}else{if(null===e[i][s+1]){h+=p.move(r,n);var y=f.globals.isXNumeric?(f.globals.seriesX[a][s]-f.globals.minX)/this.xRatio:r-this.xDivision;c=c+p.line(y,m)+p.move(r,n)+"z"}null===e[i][s]&&(h+=p.move(r,n),c+=p.move(r,m)),"stepline"===x?(h=h+p.line(r,null,"H")+p.line(null,n,"V"),c=c+p.line(r,null,"H")+p.line(null,n,"V")):"straight"===x&&(h+=p.line(r,n),c+=p.line(r,n)),s===e[i].length-2&&(c=c+p.line(r,m)+p.move(r,n)+"z",d.push(h),g.push(c))}return{linePaths:d,areaPaths:g,pX:o,pY:l,linePath:h,areaPath:c}}},{key:"handleNullDataPoints",value:function(t,e,i,a,s){var r=this.w;if(null===t[i][a]&&r.config.markers.showNullDataPoints||1===t[i].length){var n=this.markers.plotChartMarkers(e,s,a+1,this.strokeWidth-r.config.markers.strokeWidth/2,!0);null!==n&&this.elPointsMain.add(n)}}}]),t}(),zt=function(){function t(i){e(this,t),this.ctx=i,this.w=i.w,this.timeScaleArray=[],this.utc=this.w.config.xaxis.labels.datetimeUTC}return a(t,[{key:"calculateTimeScaleTicks",value:function(t,e){var i=this,a=this.w;if(a.globals.allSeriesCollapsed)return a.globals.labels=[],a.globals.timescaleLabels=[],[];var s=new R(this.ctx),r=(e-t)/864e5;this.determineInterval(r),a.globals.disableZoomIn=!1,a.globals.disableZoomOut=!1,r<.005?a.globals.disableZoomIn=!0:r>5e4&&(a.globals.disableZoomOut=!0);var o=s.getTimeUnitsfromTimestamp(t,e,this.utc),l=a.globals.gridWidth/r,h=l/24,c=h/60,d=Math.floor(24*r),g=Math.floor(24*r*60),u=Math.floor(r),f=Math.floor(r/30),p=Math.floor(r/365),x={minMinute:o.minMinute,minHour:o.minHour,minDate:o.minDate,minMonth:o.minMonth,minYear:o.minYear},b={firstVal:x,currentMinute:x.minMinute,currentHour:x.minHour,currentMonthDate:x.minDate,currentDate:x.minDate,currentMonth:x.minMonth,currentYear:x.minYear,daysWidthOnXAxis:l,hoursWidthOnXAxis:h,minutesWidthOnXAxis:c,numberOfMinutes:g,numberOfHours:d,numberOfDays:u,numberOfMonths:f,numberOfYears:p};switch(this.tickInterval){case"years":this.generateYearScale(b);break;case"months":case"half_year":this.generateMonthScale(b);break;case"months_days":case"months_fortnight":case"days":case"week_days":this.generateDayScale(b);break;case"hours":this.generateHourScale(b);break;case"minutes":this.generateMinuteScale(b)}var m=this.timeScaleArray.map((function(t){var e={position:t.position,unit:t.unit,year:t.year,day:t.day?t.day:1,hour:t.hour?t.hour:0,month:t.month+1};return"month"===t.unit?n(n({},e),{},{day:1,value:t.value+1}):"day"===t.unit||"hour"===t.unit?n(n({},e),{},{value:t.value}):"minute"===t.unit?n(n({},e),{},{value:t.value,minute:t.value}):t}));return m.filter((function(t){var e=1,s=Math.ceil(a.globals.gridWidth/120),r=t.value;void 0!==a.config.xaxis.tickAmount&&(s=a.config.xaxis.tickAmount),m.length>s&&(e=Math.floor(m.length/s));var n=!1,o=!1;switch(i.tickInterval){case"years":"year"===t.unit&&(n=!0);break;case"half_year":e=7,"year"===t.unit&&(n=!0);break;case"months":e=1,"year"===t.unit&&(n=!0);break;case"months_fortnight":e=15,"year"!==t.unit&&"month"!==t.unit||(n=!0),30===r&&(o=!0);break;case"months_days":e=10,"month"===t.unit&&(n=!0),30===r&&(o=!0);break;case"week_days":e=8,"month"===t.unit&&(n=!0);break;case"days":e=1,"month"===t.unit&&(n=!0);break;case"hours":"day"===t.unit&&(n=!0);break;case"minutes":r%5!=0&&(o=!0)}if("minutes"===i.tickInterval||"hours"===i.tickInterval){if(!o)return!0}else if((r%e==0||n)&&!o)return!0}))}},{key:"recalcDimensionsBasedOnFormat",value:function(t,e){var i=this.w,a=this.formatDates(t),s=this.removeOverlappingTS(a);i.globals.timescaleLabels=s.slice(),new lt(this.ctx).plotCoords()}},{key:"determineInterval",value:function(t){switch(!0){case t>1825:this.tickInterval="years";break;case t>800&&t<=1825:this.tickInterval="half_year";break;case t>180&&t<=800:this.tickInterval="months";break;case t>90&&t<=180:this.tickInterval="months_fortnight";break;case t>60&&t<=90:this.tickInterval="months_days";break;case t>30&&t<=60:this.tickInterval="week_days";break;case t>2&&t<=30:this.tickInterval="days";break;case t>.1&&t<=2:this.tickInterval="hours";break;case t<.1:this.tickInterval="minutes";break;default:this.tickInterval="days"}}},{key:"generateYearScale",value:function(t){var e=t.firstVal,i=t.currentMonth,a=t.currentYear,s=t.daysWidthOnXAxis,r=t.numberOfYears,n=e.minYear,o=0,l=new R(this.ctx);if(e.minDate>1||e.minMonth>0){var h=l.determineRemainingDaysOfYear(e.minYear,e.minMonth,e.minDate);o=(l.determineDaysOfYear(e.minYear)-h+1)*s,n=e.minYear+1,this.timeScaleArray.push({position:o,value:n,unit:"year",year:n,month:f.monthMod(i+1)})}else 1===e.minDate&&0===e.minMonth&&this.timeScaleArray.push({position:o,value:n,unit:"year",year:a,month:f.monthMod(i+1)});for(var c=n,d=o,g=0;g<r;g++)c++,d=l.determineDaysOfYear(c-1)*s+d,this.timeScaleArray.push({position:d,value:c,unit:"year",year:c,month:1})}},{key:"generateMonthScale",value:function(t){var e=t.firstVal,i=t.currentMonthDate,a=t.currentMonth,s=t.currentYear,r=t.daysWidthOnXAxis,n=t.numberOfMonths,o=a,l=0,h=new R(this.ctx),c="month",d=0;if(e.minDate>1){l=(h.determineDaysOfMonths(a+1,e.minYear)-i+1)*r,o=f.monthMod(a+1);var g=s+d,u=f.monthMod(o),p=o;0===o&&(c="year",p=g,u=1,g+=d+=1),this.timeScaleArray.push({position:l,value:p,unit:c,year:g,month:u})}else this.timeScaleArray.push({position:l,value:o,unit:c,year:s,month:f.monthMod(a)});for(var x=o+1,b=l,m=0,v=1;m<n;m++,v++){0===(x=f.monthMod(x))?(c="year",d+=1):c="month";var y=this._getYear(s,x,d);b=h.determineDaysOfMonths(x,y)*r+b;var w=0===x?y:x;this.timeScaleArray.push({position:b,value:w,unit:c,year:y,month:0===x?1:x}),x++}}},{key:"generateDayScale",value:function(t){var e=t.firstVal,i=t.currentMonth,a=t.currentYear,s=t.hoursWidthOnXAxis,r=t.numberOfDays,n=new R(this.ctx),o="day",l=e.minDate+1,h=l,c=function(t,e,i){return t>n.determineDaysOfMonths(e+1,i)?(h=1,o="month",g=e+=1,e):e},d=(24-e.minHour)*s,g=l,u=c(h,i,a);0===e.minHour&&1===e.minDate&&(d=0,g=f.monthMod(e.minMonth),o="month",h=e.minDate,r++),this.timeScaleArray.push({position:d,value:g,unit:o,year:this._getYear(a,u,0),month:f.monthMod(u),day:h});for(var p=d,x=0;x<r;x++){o="day",u=c(h+=1,u,this._getYear(a,u,0));var b=this._getYear(a,u,0);p=24*s+p;var m=1===h?f.monthMod(u):h;this.timeScaleArray.push({position:p,value:m,unit:o,year:b,month:f.monthMod(u),day:m})}}},{key:"generateHourScale",value:function(t){var e=t.firstVal,i=t.currentDate,a=t.currentMonth,s=t.currentYear,r=t.minutesWidthOnXAxis,n=t.numberOfHours,o=new R(this.ctx),l="hour",h=function(t,e){return t>o.determineDaysOfMonths(e+1,s)&&(x=1,e+=1),{month:e,date:x}},c=function(t,e){return t>o.determineDaysOfMonths(e+1,s)?e+=1:e},d=60-e.minMinute,g=d*r,u=e.minHour+1,p=u+1;60===d&&(g=0,p=(u=e.minHour)+1);var x=i,b=c(x,a);this.timeScaleArray.push({position:g,value:u,unit:l,day:x,hour:p,year:s,month:f.monthMod(b)});for(var m=g,v=0;v<n;v++){if(l="hour",p>=24)p=0,l="day",b=h(x+=1,b).month,b=c(x,b);var y=this._getYear(s,b,0);m=0===p&&0===v?d*r:60*r+m;var w=0===p?x:p;this.timeScaleArray.push({position:m,value:w,unit:l,hour:p,day:x,year:y,month:f.monthMod(b)}),p++}}},{key:"generateMinuteScale",value:function(t){var e=t.firstVal,i=t.currentMinute,a=t.currentHour,s=t.currentDate,r=t.currentMonth,n=t.currentYear,o=t.minutesWidthOnXAxis,l=t.numberOfMinutes,h=o-(i-e.minMinute),c=e.minMinute+1,d=c+1,g=s,u=r,p=n,x=a;this.timeScaleArray.push({position:h,value:c,unit:"minute",day:g,hour:x,minute:d,year:p,month:f.monthMod(u)});for(var b=h,m=0;m<l;m++)d>=60&&(d=0,24===(x+=1)&&(x=0)),b=o+b,this.timeScaleArray.push({position:b,value:d,unit:"minute",hour:x,minute:d,day:g,year:this._getYear(n,u,0),month:f.monthMod(u)}),d++}},{key:"createRawDateString",value:function(t,e){var i=t.year;return i+="-"+("0"+t.month.toString()).slice(-2),"day"===t.unit?i+="day"===t.unit?"-"+("0"+e).slice(-2):"-01":i+="-"+("0"+(t.day?t.day:"1")).slice(-2),"hour"===t.unit?i+="hour"===t.unit?"T"+("0"+e).slice(-2):"T00":i+="T"+("0"+(t.hour?t.hour:"0")).slice(-2),i+="minute"===t.unit?":"+("0"+e).slice(-2)+":00":":00:00",this.utc&&(i+=".000Z"),i}},{key:"formatDates",value:function(t){var e=this,i=this.w;return t.map((function(t){var a=t.value.toString(),s=new R(e.ctx),r=e.createRawDateString(t,a),n=s.getDate(r);if(void 0===i.config.xaxis.labels.format){var o="dd MMM",l=i.config.xaxis.labels.datetimeFormatter;"year"===t.unit&&(o=l.year),"month"===t.unit&&(o=l.month),"day"===t.unit&&(o=l.day),"hour"===t.unit&&(o=l.hour),"minute"===t.unit&&(o=l.minute),a=s.formatDate(n,o)}else a=s.formatDate(n,i.config.xaxis.labels.format);return{dateString:r,position:t.position,value:a,unit:t.unit,year:t.year,month:t.month}}))}},{key:"removeOverlappingTS",value:function(t){var e,i=this,a=new b(this.ctx),s=!1;t.length>0&&t[0].value&&t.every((function(e){return e.value.length===t[0].value.length}))&&(s=!0,e=a.getTextRects(t[0].value).width);var r=0,n=t.map((function(n,o){if(o>0&&i.w.config.xaxis.labels.hideOverlappingLabels){var l=s?e:a.getTextRects(t[r].value).width,h=t[r].position;return n.position>h+l+10?(r=o,n):null}return n}));return n=n.filter((function(t){return null!==t}))}},{key:"_getYear",value:function(t,e,i){return t+Math.floor(e/12)+i}}]),t}(),It=function(){function t(i,a){e(this,t),this.ctx=a,this.w=a.w,this.el=i}return a(t,[{key:"setupElements",value:function(){var t=this.w.globals,e=this.w.config,i=e.chart.type;t.axisCharts=["line","area","bar","rangeBar","candlestick","scatter","bubble","radar","heatmap"].indexOf(i)>-1,t.xyCharts=["line","area","bar","rangeBar","candlestick","scatter","bubble"].indexOf(i)>-1,t.isBarHorizontal=("bar"===e.chart.type||"rangeBar"===e.chart.type)&&e.plotOptions.bar.horizontal,t.chartClass=".apexcharts"+t.cuid,t.dom.baseEl=this.el,t.dom.elWrap=document.createElement("div"),b.setAttrs(t.dom.elWrap,{id:t.chartClass.substring(1),class:"apexcharts-canvas "+t.chartClass.substring(1)}),this.el.appendChild(t.dom.elWrap),t.dom.Paper=new window.SVG.Doc(t.dom.elWrap),t.dom.Paper.attr({class:"apexcharts-svg","xmlns:data":"ApexChartsNS",transform:"translate(".concat(e.chart.offsetX,", ").concat(e.chart.offsetY,")")}),t.dom.Paper.node.style.background=e.chart.background,this.setSVGDimensions(),t.dom.elGraphical=t.dom.Paper.group().attr({class:"apexcharts-inner apexcharts-graphical"}),t.dom.elAnnotations=t.dom.Paper.group().attr({class:"apexcharts-annotations"}),t.dom.elDefs=t.dom.Paper.defs(),t.dom.elLegendWrap=document.createElement("div"),t.dom.elLegendWrap.classList.add("apexcharts-legend"),t.dom.elWrap.appendChild(t.dom.elLegendWrap),t.dom.Paper.add(t.dom.elGraphical),t.dom.elGraphical.add(t.dom.elDefs)}},{key:"plotChartType",value:function(t,e){var i=this.w,a=i.config,s=i.globals,r={series:[],i:[]},n={series:[],i:[]},o={series:[],i:[]},l={series:[],i:[]},h={series:[],i:[]},c={series:[],i:[]};s.series.map((function(e,d){void 0!==t[d].type?("column"===t[d].type||"bar"===t[d].type?(s.series.length>1&&a.plotOptions.bar.horizontal&&console.warn("Horizontal bars are not supported in a mixed/combo chart. Please turn off `plotOptions.bar.horizontal`"),h.series.push(e),h.i.push(d),i.globals.columnSeries=h.series):"area"===t[d].type?(n.series.push(e),n.i.push(d)):"line"===t[d].type?(r.series.push(e),r.i.push(d)):"scatter"===t[d].type?(o.series.push(e),o.i.push(d)):"bubble"===t[d].type?(l.series.push(e),l.i.push(d)):"candlestick"===t[d].type?(c.series.push(e),c.i.push(d)):console.warn("You have specified an unrecognized chart type. Available types for this propery are line/area/column/bar/scatter/bubble"),s.comboCharts=!0):(r.series.push(e),r.i.push(d))}));var d=new Tt(this.ctx,e),g=new wt(this.ctx,e);this.ctx.pie=new St(this.ctx);var u=new Lt(this.ctx),f=new D(this.ctx,e),p=new Ct(this.ctx),x=[];if(s.comboCharts){if(n.series.length>0&&x.push(d.draw(n.series,"area",n.i)),h.series.length>0)if(i.config.chart.stacked){var b=new yt(this.ctx,e);x.push(b.draw(h.series,h.i))}else{var m=new F(this.ctx,e);x.push(m.draw(h.series,h.i))}if(r.series.length>0&&x.push(d.draw(r.series,"line",r.i)),c.series.length>0&&x.push(g.draw(c.series,c.i)),o.series.length>0){var v=new Tt(this.ctx,e,!0);x.push(v.draw(o.series,"scatter",o.i))}if(l.series.length>0){var y=new Tt(this.ctx,e,!0);x.push(y.draw(l.series,"bubble",l.i))}}else switch(a.chart.type){case"line":x=d.draw(s.series,"line");break;case"area":x=d.draw(s.series,"area");break;case"bar":if(a.chart.stacked)x=new yt(this.ctx,e).draw(s.series);else x=new F(this.ctx,e).draw(s.series);break;case"candlestick":x=new wt(this.ctx,e).draw(s.series);break;case"rangeBar":x=f.draw(s.series);break;case"heatmap":x=new kt(this.ctx,e).draw(s.series);break;case"pie":case"donut":case"polarArea":x=this.ctx.pie.draw(s.series);break;case"radialBar":x=u.draw(s.series);break;case"radar":x=p.draw(s.series);break;default:x=d.draw(s.series)}return x}},{key:"setSVGDimensions",value:function(){var t=this.w.globals,e=this.w.config;t.svgWidth=e.chart.width,t.svgHeight=e.chart.height;var i=f.getDimensions(this.el),a=e.chart.width.toString().split(/[0-9]+/g).pop();if("%"===a?f.isNumber(i[0])&&(0===i[0].width&&(i=f.getDimensions(this.el.parentNode)),t.svgWidth=i[0]*parseInt(e.chart.width,10)/100):"px"!==a&&""!==a||(t.svgWidth=parseInt(e.chart.width,10)),"auto"!==t.svgHeight&&""!==t.svgHeight)if("%"===e.chart.height.toString().split(/[0-9]+/g).pop()){var s=f.getDimensions(this.el.parentNode);t.svgHeight=s[1]*parseInt(e.chart.height,10)/100}else t.svgHeight=parseInt(e.chart.height,10);else t.axisCharts?t.svgHeight=t.svgWidth/1.61:t.svgHeight=t.svgWidth/1.2;t.svgWidth<0&&(t.svgWidth=0),t.svgHeight<0&&(t.svgHeight=0),b.setAttrs(t.dom.Paper.node,{width:t.svgWidth,height:t.svgHeight});var r=e.chart.sparkline.enabled?0:t.axisCharts?e.chart.parentHeightOffset:0;t.dom.Paper.node.parentNode.parentNode.style.minHeight=t.svgHeight+r+"px",t.dom.elWrap.style.width=t.svgWidth+"px",t.dom.elWrap.style.height=t.svgHeight+"px"}},{key:"shiftGraphPosition",value:function(){var t=this.w.globals,e=t.translateY,i={transform:"translate("+t.translateX+", "+e+")"};b.setAttrs(t.dom.elGraphical.node,i)}},{key:"resizeNonAxisCharts",value:function(){var t=this.w,e=t.globals,i=0,a=t.config.chart.sparkline.enabled?1:15;a+=t.config.grid.padding.bottom,"top"!==t.config.legend.position&&"bottom"!==t.config.legend.position||!t.config.legend.show||t.config.legend.floating||(i=new ct(this.ctx).legendHelpers.getLegendBBox().clwh+10);var s=t.globals.dom.baseEl.querySelector(".apexcharts-radialbar"),r=2.05*t.globals.radialSize;if(s&&!t.config.chart.sparkline.enabled){var n=f.getBoundingClientRect(s);r=n.bottom;var o=n.bottom-n.top;r=Math.max(2.05*t.globals.radialSize,o)}var l=r+e.translateY+i+a;e.dom.elLegendForeign&&e.dom.elLegendForeign.setAttribute("height",l),e.dom.elWrap.style.height=l+"px",b.setAttrs(e.dom.Paper.node,{height:l}),e.dom.Paper.node.parentNode.parentNode.style.minHeight=l+"px"}},{key:"coreCalculations",value:function(){new Z(this.ctx).init()}},{key:"resetGlobals",value:function(){var t=this,e=function(){return t.w.config.series.map((function(t){return[]}))},i=new O,a=this.w.globals;i.initGlobalVars(a),a.seriesXvalues=e(),a.seriesYvalues=e()}},{key:"isMultipleY",value:function(){if(this.w.config.yaxis.constructor===Array&&this.w.config.yaxis.length>1)return this.w.globals.isMultipleYAxis=!0,!0}},{key:"xySettings",value:function(){var t=null,e=this.w;if(e.globals.axisCharts){if("back"===e.config.xaxis.crosshairs.position)new tt(this.ctx).drawXCrosshairs();if("back"===e.config.yaxis[0].crosshairs.position)new tt(this.ctx).drawYCrosshairs();if("datetime"===e.config.xaxis.type&&void 0===e.config.xaxis.labels.formatter){var i=new zt(this.ctx),a=[];isFinite(e.globals.minX)&&isFinite(e.globals.maxX)&&!e.globals.isBarHorizontal?a=i.calculateTimeScaleTicks(e.globals.minX,e.globals.maxX):e.globals.isBarHorizontal&&(a=i.calculateTimeScaleTicks(e.globals.minY,e.globals.maxY)),i.recalcDimensionsBasedOnFormat(a)}t=new y(this.ctx).getCalculatedRatios()}return t}},{key:"updateSourceChart",value:function(t){this.ctx.w.globals.selection=void 0,this.ctx.updateHelpers._updateOptions({chart:{selection:{xaxis:{min:t.w.globals.minX,max:t.w.globals.maxX}}}},!1,!1)}},{key:"setupBrushHandler",value:function(){var t=this,e=this.w;if(e.config.chart.brush.enabled&&"function"!=typeof e.config.chart.events.selection){var i=e.config.chart.brush.targets||[e.config.chart.brush.target];i.forEach((function(e){var i=ApexCharts.getChartByID(e);i.w.globals.brushSource=t.ctx,"function"!=typeof i.w.config.chart.events.zoomed&&(i.w.config.chart.events.zoomed=function(){t.updateSourceChart(i)}),"function"!=typeof i.w.config.chart.events.scrolled&&(i.w.config.chart.events.scrolled=function(){t.updateSourceChart(i)})})),e.config.chart.events.selection=function(t,a){i.forEach((function(t){var i=ApexCharts.getChartByID(t),s=f.clone(e.config.yaxis);if(e.config.chart.brush.autoScaleYaxis&&1===i.w.globals.series.length){var r=new q(i);s=r.autoScaleY(i,s,a)}var o=i.w.config.yaxis.reduce((function(t,e,a){return[].concat(g(t),[n(n({},i.w.config.yaxis[a]),{},{min:s[0].min,max:s[0].max})])}),[]);i.ctx.updateHelpers._updateOptions({xaxis:{min:a.xaxis.min,max:a.xaxis.max},yaxis:o},!1,!1,!1,!1)}))}}}}]),t}(),Mt=function(){function i(t){e(this,i),this.ctx=t,this.w=t.w}return a(i,[{key:"_updateOptions",value:function(e){var i=this,a=arguments.length>1&&void 0!==arguments[1]&&arguments[1],s=!(arguments.length>2&&void 0!==arguments[2])||arguments[2],r=!(arguments.length>3&&void 0!==arguments[3])||arguments[3],n=arguments.length>4&&void 0!==arguments[4]&&arguments[4],o=[this.ctx];r&&(o=this.ctx.getSyncedCharts()),this.ctx.w.globals.isExecCalled&&(o=[this.ctx],this.ctx.w.globals.isExecCalled=!1),o.forEach((function(r){var o=r.w;return o.globals.shouldAnimate=s,a||(o.globals.resized=!0,o.globals.dataChanged=!0,s&&r.series.getPreviousPaths()),e&&"object"===t(e)&&(r.config=new N(e),e=y.extendArrayProps(r.config,e,o),r.w.globals.chartID!==i.ctx.w.globals.chartID&&delete e.series,o.config=f.extend(o.config,e),n&&(o.globals.lastXAxis=[],o.globals.lastYAxis=[],o.globals.initialConfig=f.extend({},o.config),o.globals.initialSeries=f.clone(o.config.series))),r.update(e)}))}},{key:"_updateSeries",value:function(t,e){var i,a=this,s=arguments.length>2&&void 0!==arguments[2]&&arguments[2],r=this.w;return r.globals.shouldAnimate=e,r.globals.dataChanged=!0,e&&this.ctx.series.getPreviousPaths(),r.globals.axisCharts?(0===(i=t.map((function(t,e){return a._extendSeries(t,e)}))).length&&(i=[{data:[]}]),r.config.series=i):r.config.series=t.slice(),s&&(r.globals.initialSeries=f.clone(r.config.series)),this.ctx.update()}},{key:"_extendSeries",value:function(t,e){var i=this.w;return n(n({},i.config.series[e]),{},{name:t.name?t.name:i.config.series[e]&&i.config.series[e].name,type:t.type?t.type:i.config.series[e]&&i.config.series[e].type,data:t.data?t.data:i.config.series[e]&&i.config.series[e].data})}},{key:"toggleDataPointSelection",value:function(t,e){var i=this.w,a=null,s=".apexcharts-series[data\\:realIndex='".concat(t,"']");return i.globals.axisCharts?a=i.globals.dom.Paper.select("".concat(s," path[j='").concat(e,"'], ").concat(s," circle[j='").concat(e,"'], ").concat(s," rect[j='").concat(e,"']")).members[0]:void 0===e&&(a=i.globals.dom.Paper.select("".concat(s," path[j='").concat(t,"']")).members[0],"pie"!==i.config.chart.type&&"polarArea"!==i.config.chart.type&&"donut"!==i.config.chart.type||this.ctx.pie.pieClicked(t)),a?(new b(this.ctx).pathMouseDown(a,null),a.node?a.node:null):(console.warn("toggleDataPointSelection: Element not found"),null)}},{key:"forceXAxisUpdate",value:function(t){var e=this.w;if(["min","max"].forEach((function(i){void 0!==t.xaxis[i]&&(e.config.xaxis[i]=t.xaxis[i],e.globals.lastXAxis[i]=t.xaxis[i])})),t.xaxis.categories&&t.xaxis.categories.length&&(e.config.xaxis.categories=t.xaxis.categories),e.config.xaxis.convertedCatToNumeric){var i=new H(t);t=i.convertCatToNumericXaxis(t,this.ctx)}return t}},{key:"forceYAxisUpdate",value:function(t){var e=this.w;return e.config.chart.stacked&&"100%"===e.config.chart.stackType&&(Array.isArray(t.yaxis)?t.yaxis.forEach((function(e,i){t.yaxis[i].min=0,t.yaxis[i].max=100})):(t.yaxis.min=0,t.yaxis.max=100)),t}},{key:"revertDefaultAxisMinMax",value:function(){var t=this,e=this.w;e.config.xaxis.min=e.globals.lastXAxis.min,e.config.xaxis.max=e.globals.lastXAxis.max,e.config.yaxis.map((function(i,a){e.globals.zoomed?void 0!==e.globals.lastYAxis[a]&&(i.min=e.globals.lastYAxis[a].min,i.max=e.globals.lastYAxis[a].max):void 0!==t.ctx.opts.yaxis[a]&&(i.min=t.ctx.opts.yaxis[a].min,i.max=t.ctx.opts.yaxis[a].max)}))}}]),i}();A="undefined"!=typeof window?window:void 0,S=function(e,i){var a=(void 0!==this?this:e).SVG=function(t){if(a.supported)return t=new a.Doc(t),a.parser.draw||a.prepare(),t};if(a.ns="http://www.w3.org/2000/svg",a.xmlns="http://www.w3.org/2000/xmlns/",a.xlink="http://www.w3.org/1999/xlink",a.svgjs="http://svgjs.com/svgjs",a.supported=!0,!a.supported)return!1;a.did=1e3,a.eid=function(t){return"Svgjs"+d(t)+a.did++},a.create=function(t){var e=i.createElementNS(this.ns,t);return e.setAttribute("id",this.eid(t)),e},a.extend=function(){var t,e;e=(t=[].slice.call(arguments)).pop();for(var i=t.length-1;i>=0;i--)if(t[i])for(var s in e)t[i].prototype[s]=e[s];a.Set&&a.Set.inherit&&a.Set.inherit()},a.invent=function(t){var e="function"==typeof t.create?t.create:function(){this.constructor.call(this,a.create(t.create))};return t.inherit&&(e.prototype=new t.inherit),t.extend&&a.extend(e,t.extend),t.construct&&a.extend(t.parent||a.Container,t.construct),e},a.adopt=function(t){return t?t.instance?t.instance:((i="svg"==t.nodeName?t.parentNode instanceof e.SVGElement?new a.Nested:new a.Doc:"linearGradient"==t.nodeName?new a.Gradient("linear"):"radialGradient"==t.nodeName?new a.Gradient("radial"):a[d(t.nodeName)]?new(a[d(t.nodeName)]):new a.Element(t)).type=t.nodeName,i.node=t,t.instance=i,i instanceof a.Doc&&i.namespace().defs(),i.setData(JSON.parse(t.getAttribute("svgjs:data"))||{}),i):null;var i},a.prepare=function(){var t=i.getElementsByTagName("body")[0],e=(t?new a.Doc(t):a.adopt(i.documentElement).nested()).size(2,0);a.parser={body:t||i.documentElement,draw:e.style("opacity:0;position:absolute;left:-100%;top:-100%;overflow:hidden").node,poly:e.polyline().node,path:e.path().node,native:a.create("svg")}},a.parser={native:a.create("svg")},i.addEventListener("DOMContentLoaded",(function(){a.parser.draw||a.prepare()}),!1),a.regex={numberAndUnit:/^([+-]?(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?)([a-z%]*)$/i,hex:/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i,rgb:/rgb\((\d+),(\d+),(\d+)\)/,reference:/#([a-z0-9\-_]+)/i,transforms:/\)\s*,?\s*/,whitespace:/\s/g,isHex:/^#[a-f0-9]{3,6}$/i,isRgb:/^rgb\(/,isCss:/[^:]+:[^;]+;?/,isBlank:/^(\s+)?$/,isNumber:/^[+-]?(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?$/i,isPercent:/^-?[\d\.]+%$/,isImage:/\.(jpg|jpeg|png|gif|svg)(\?[^=]+.*)?/i,delimiter:/[\s,]+/,hyphen:/([^e])\-/gi,pathLetters:/[MLHVCSQTAZ]/gi,isPathLetter:/[MLHVCSQTAZ]/i,numbersWithDots:/((\d?\.\d+(?:e[+-]?\d+)?)((?:\.\d+(?:e[+-]?\d+)?)+))+/gi,dots:/\./g},a.utils={map:function(t,e){for(var i=t.length,a=[],s=0;s<i;s++)a.push(e(t[s]));return a},filter:function(t,e){for(var i=t.length,a=[],s=0;s<i;s++)e(t[s])&&a.push(t[s]);return a},filterSVGElements:function(t){return this.filter(t,(function(t){return t instanceof e.SVGElement}))}},a.defaults={attrs:{"fill-opacity":1,"stroke-opacity":1,"stroke-width":0,"stroke-linejoin":"miter","stroke-linecap":"butt",fill:"#000000",stroke:"#000000",opacity:1,x:0,y:0,cx:0,cy:0,width:0,height:0,r:0,rx:0,ry:0,offset:0,"stop-opacity":1,"stop-color":"#000000","font-size":16,"font-family":"Helvetica, Arial, sans-serif","text-anchor":"start"}},a.Color=function(e){var i,s;this.r=0,this.g=0,this.b=0,e&&("string"==typeof e?a.regex.isRgb.test(e)?(i=a.regex.rgb.exec(e.replace(a.regex.whitespace,"")),this.r=parseInt(i[1]),this.g=parseInt(i[2]),this.b=parseInt(i[3])):a.regex.isHex.test(e)&&(i=a.regex.hex.exec(4==(s=e).length?["#",s.substring(1,2),s.substring(1,2),s.substring(2,3),s.substring(2,3),s.substring(3,4),s.substring(3,4)].join(""):s),this.r=parseInt(i[1],16),this.g=parseInt(i[2],16),this.b=parseInt(i[3],16)):"object"===t(e)&&(this.r=e.r,this.g=e.g,this.b=e.b))},a.extend(a.Color,{toString:function(){return this.toHex()},toHex:function(){return"#"+g(this.r)+g(this.g)+g(this.b)},toRgb:function(){return"rgb("+[this.r,this.g,this.b].join()+")"},brightness:function(){return this.r/255*.3+this.g/255*.59+this.b/255*.11},morph:function(t){return this.destination=new a.Color(t),this},at:function(t){return this.destination?(t=t<0?0:t>1?1:t,new a.Color({r:~~(this.r+(this.destination.r-this.r)*t),g:~~(this.g+(this.destination.g-this.g)*t),b:~~(this.b+(this.destination.b-this.b)*t)})):this}}),a.Color.test=function(t){return t+="",a.regex.isHex.test(t)||a.regex.isRgb.test(t)},a.Color.isRgb=function(t){return t&&"number"==typeof t.r&&"number"==typeof t.g&&"number"==typeof t.b},a.Color.isColor=function(t){return a.Color.isRgb(t)||a.Color.test(t)},a.Array=function(t,e){0==(t=(t||[]).valueOf()).length&&e&&(t=e.valueOf()),this.value=this.parse(t)},a.extend(a.Array,{toString:function(){return this.value.join(" ")},valueOf:function(){return this.value},parse:function(t){return t=t.valueOf(),Array.isArray(t)?t:this.split(t)}}),a.PointArray=function(t,e){a.Array.call(this,t,e||[[0,0]])},a.PointArray.prototype=new a.Array,a.PointArray.prototype.constructor=a.PointArray;for(var s={M:function(t,e,i){return e.x=i.x=t[0],e.y=i.y=t[1],["M",e.x,e.y]},L:function(t,e){return e.x=t[0],e.y=t[1],["L",t[0],t[1]]},H:function(t,e){return e.x=t[0],["H",t[0]]},V:function(t,e){return e.y=t[0],["V",t[0]]},C:function(t,e){return e.x=t[4],e.y=t[5],["C",t[0],t[1],t[2],t[3],t[4],t[5]]},Q:function(t,e){return e.x=t[2],e.y=t[3],["Q",t[0],t[1],t[2],t[3]]},Z:function(t,e,i){return e.x=i.x,e.y=i.y,["Z"]}},r="mlhvqtcsaz".split(""),n=0,o=r.length;n<o;++n)s[r[n]]=function(t){return function(e,i,a){if("H"==t)e[0]=e[0]+i.x;else if("V"==t)e[0]=e[0]+i.y;else if("A"==t)e[5]=e[5]+i.x,e[6]=e[6]+i.y;else for(var r=0,n=e.length;r<n;++r)e[r]=e[r]+(r%2?i.y:i.x);return s[t](e,i,a)}}(r[n].toUpperCase());a.PathArray=function(t,e){a.Array.call(this,t,e||[["M",0,0]])},a.PathArray.prototype=new a.Array,a.PathArray.prototype.constructor=a.PathArray,a.extend(a.PathArray,{toString:function(){return function(t){for(var e=0,i=t.length,a="";e<i;e++)a+=t[e][0],null!=t[e][1]&&(a+=t[e][1],null!=t[e][2]&&(a+=" ",a+=t[e][2],null!=t[e][3]&&(a+=" ",a+=t[e][3],a+=" ",a+=t[e][4],null!=t[e][5]&&(a+=" ",a+=t[e][5],a+=" ",a+=t[e][6],null!=t[e][7]&&(a+=" ",a+=t[e][7])))));return a+" "}(this.value)},move:function(t,e){var i=this.bbox();return i.x,i.y,this},at:function(t){if(!this.destination)return this;for(var e=this.value,i=this.destination.value,s=[],r=new a.PathArray,n=0,o=e.length;n<o;n++){s[n]=[e[n][0]];for(var l=1,h=e[n].length;l<h;l++)s[n][l]=e[n][l]+(i[n][l]-e[n][l])*t;"A"===s[n][0]&&(s[n][4]=+(0!=s[n][4]),s[n][5]=+(0!=s[n][5]))}return r.value=s,r},parse:function(t){if(t instanceof a.PathArray)return t.valueOf();var e,i={M:2,L:2,H:1,V:1,C:6,S:4,Q:4,T:2,A:7,Z:0};t="string"==typeof t?t.replace(a.regex.numbersWithDots,h).replace(a.regex.pathLetters," $& ").replace(a.regex.hyphen,"$1 -").trim().split(a.regex.delimiter):t.reduce((function(t,e){return[].concat.call(t,e)}),[]);var r=[],n=new a.Point,o=new a.Point,l=0,c=t.length;do{a.regex.isPathLetter.test(t[l])?(e=t[l],++l):"M"==e?e="L":"m"==e&&(e="l"),r.push(s[e].call(null,t.slice(l,l+=i[e.toUpperCase()]).map(parseFloat),n,o))}while(c>l);return r},bbox:function(){return a.parser.draw||a.prepare(),a.parser.path.setAttribute("d",this.toString()),a.parser.path.getBBox()}}),a.Number=a.invent({create:function(t,e){this.value=0,this.unit=e||"","number"==typeof t?this.value=isNaN(t)?0:isFinite(t)?t:t<0?-34e37:34e37:"string"==typeof t?(e=t.match(a.regex.numberAndUnit))&&(this.value=parseFloat(e[1]),"%"==e[5]?this.value/=100:"s"==e[5]&&(this.value*=1e3),this.unit=e[5]):t instanceof a.Number&&(this.value=t.valueOf(),this.unit=t.unit)},extend:{toString:function(){return("%"==this.unit?~~(1e8*this.value)/1e6:"s"==this.unit?this.value/1e3:this.value)+this.unit},toJSON:function(){return this.toString()},valueOf:function(){return this.value},plus:function(t){return t=new a.Number(t),new a.Number(this+t,this.unit||t.unit)},minus:function(t){return t=new a.Number(t),new a.Number(this-t,this.unit||t.unit)},times:function(t){return t=new a.Number(t),new a.Number(this*t,this.unit||t.unit)},divide:function(t){return t=new a.Number(t),new a.Number(this/t,this.unit||t.unit)},to:function(t){var e=new a.Number(this);return"string"==typeof t&&(e.unit=t),e},morph:function(t){return this.destination=new a.Number(t),t.relative&&(this.destination.value+=this.value),this},at:function(t){return this.destination?new a.Number(this.destination).minus(this).times(t).plus(this):this}}}),a.Element=a.invent({create:function(t){this._stroke=a.defaults.attrs.stroke,this._event=null,this.dom={},(this.node=t)&&(this.type=t.nodeName,this.node.instance=this,this._stroke=t.getAttribute("stroke")||this._stroke)},extend:{x:function(t){return this.attr("x",t)},y:function(t){return this.attr("y",t)},cx:function(t){return null==t?this.x()+this.width()/2:this.x(t-this.width()/2)},cy:function(t){return null==t?this.y()+this.height()/2:this.y(t-this.height()/2)},move:function(t,e){return this.x(t).y(e)},center:function(t,e){return this.cx(t).cy(e)},width:function(t){return this.attr("width",t)},height:function(t){return this.attr("height",t)},size:function(t,e){var i=u(this,t,e);return this.width(new a.Number(i.width)).height(new a.Number(i.height))},clone:function(t){this.writeDataToDom();var e=x(this.node.cloneNode(!0));return t?t.add(e):this.after(e),e},remove:function(){return this.parent()&&this.parent().removeElement(this),this},replace:function(t){return this.after(t).remove(),t},addTo:function(t){return t.put(this)},putIn:function(t){return t.add(this)},id:function(t){return this.attr("id",t)},show:function(){return this.style("display","")},hide:function(){return this.style("display","none")},visible:function(){return"none"!=this.style("display")},toString:function(){return this.attr("id")},classes:function(){var t=this.attr("class");return null==t?[]:t.trim().split(a.regex.delimiter)},hasClass:function(t){return-1!=this.classes().indexOf(t)},addClass:function(t){if(!this.hasClass(t)){var e=this.classes();e.push(t),this.attr("class",e.join(" "))}return this},removeClass:function(t){return this.hasClass(t)&&this.attr("class",this.classes().filter((function(e){return e!=t})).join(" ")),this},toggleClass:function(t){return this.hasClass(t)?this.removeClass(t):this.addClass(t)},reference:function(t){return a.get(this.attr(t))},parent:function(t){var i=this;if(!i.node.parentNode)return null;if(i=a.adopt(i.node.parentNode),!t)return i;for(;i&&i.node instanceof e.SVGElement;){if("string"==typeof t?i.matches(t):i instanceof t)return i;if(!i.node.parentNode||"#document"==i.node.parentNode.nodeName)return null;i=a.adopt(i.node.parentNode)}},doc:function(){return this instanceof a.Doc?this:this.parent(a.Doc)},parents:function(t){var e=[],i=this;do{if(!(i=i.parent(t))||!i.node)break;e.push(i)}while(i.parent);return e},matches:function(t){return function(t,e){return(t.matches||t.matchesSelector||t.msMatchesSelector||t.mozMatchesSelector||t.webkitMatchesSelector||t.oMatchesSelector).call(t,e)}(this.node,t)},native:function(){return this.node},svg:function(t){var e=i.createElement("svg");if(!(t&&this instanceof a.Parent))return e.appendChild(t=i.createElement("svg")),this.writeDataToDom(),t.appendChild(this.node.cloneNode(!0)),e.innerHTML.replace(/^<svg>/,"").replace(/<\/svg>$/,"");e.innerHTML="<svg>"+t.replace(/\n/,"").replace(/<([\w:-]+)([^<]+?)\/>/g,"<$1$2></$1>")+"</svg>";for(var s=0,r=e.firstChild.childNodes.length;s<r;s++)this.node.appendChild(e.firstChild.firstChild);return this},writeDataToDom:function(){return(this.each||this.lines)&&(this.each?this:this.lines()).each((function(){this.writeDataToDom()})),this.node.removeAttribute("svgjs:data"),Object.keys(this.dom).length&&this.node.setAttribute("svgjs:data",JSON.stringify(this.dom)),this},setData:function(t){return this.dom=t,this},is:function(t){return function(t,e){return t instanceof e}(this,t)}}}),a.easing={"-":function(t){return t},"<>":function(t){return-Math.cos(t*Math.PI)/2+.5},">":function(t){return Math.sin(t*Math.PI/2)},"<":function(t){return 1-Math.cos(t*Math.PI/2)}},a.morph=function(t){return function(e,i){return new a.MorphObj(e,i).at(t)}},a.Situation=a.invent({create:function(t){this.init=!1,this.reversed=!1,this.reversing=!1,this.duration=new a.Number(t.duration).valueOf(),this.delay=new a.Number(t.delay).valueOf(),this.start=+new Date+this.delay,this.finish=this.start+this.duration,this.ease=t.ease,this.loop=0,this.loops=!1,this.animations={},this.attrs={},this.styles={},this.transforms=[],this.once={}}}),a.FX=a.invent({create:function(t){this._target=t,this.situations=[],this.active=!1,this.situation=null,this.paused=!1,this.lastPos=0,this.pos=0,this.absPos=0,this._speed=1},extend:{animate:function(e,i,s){"object"===t(e)&&(i=e.ease,s=e.delay,e=e.duration);var r=new a.Situation({duration:e||1e3,delay:s||0,ease:a.easing[i||"-"]||i});return this.queue(r),this},target:function(t){return t&&t instanceof a.Element?(this._target=t,this):this._target},timeToAbsPos:function(t){return(t-this.situation.start)/(this.situation.duration/this._speed)},absPosToTime:function(t){return this.situation.duration/this._speed*t+this.situation.start},startAnimFrame:function(){this.stopAnimFrame(),this.animationFrame=e.requestAnimationFrame(function(){this.step()}.bind(this))},stopAnimFrame:function(){e.cancelAnimationFrame(this.animationFrame)},start:function(){return!this.active&&this.situation&&(this.active=!0,this.startCurrent()),this},startCurrent:function(){return this.situation.start=+new Date+this.situation.delay/this._speed,this.situation.finish=this.situation.start+this.situation.duration/this._speed,this.initAnimations().step()},queue:function(t){return("function"==typeof t||t instanceof a.Situation)&&this.situations.push(t),this.situation||(this.situation=this.situations.shift()),this},dequeue:function(){return this.stop(),this.situation=this.situations.shift(),this.situation&&(this.situation instanceof a.Situation?this.start():this.situation.call(this)),this},initAnimations:function(){var t,e=this.situation;if(e.init)return this;for(var i in e.animations){t=this.target()[i](),Array.isArray(t)||(t=[t]),Array.isArray(e.animations[i])||(e.animations[i]=[e.animations[i]]);for(var s=t.length;s--;)e.animations[i][s]instanceof a.Number&&(t[s]=new a.Number(t[s])),e.animations[i][s]=t[s].morph(e.animations[i][s])}for(var i in e.attrs)e.attrs[i]=new a.MorphObj(this.target().attr(i),e.attrs[i]);for(var i in e.styles)e.styles[i]=new a.MorphObj(this.target().style(i),e.styles[i]);return e.initialTransformation=this.target().matrixify(),e.init=!0,this},clearQueue:function(){return this.situations=[],this},clearCurrent:function(){return this.situation=null,this},stop:function(t,e){var i=this.active;return this.active=!1,e&&this.clearQueue(),t&&this.situation&&(!i&&this.startCurrent(),this.atEnd()),this.stopAnimFrame(),this.clearCurrent()},after:function(t){var e=this.last();return this.target().on("finished.fx",(function i(a){a.detail.situation==e&&(t.call(this,e),this.off("finished.fx",i))})),this._callStart()},during:function(t){var e=this.last(),i=function(i){i.detail.situation==e&&t.call(this,i.detail.pos,a.morph(i.detail.pos),i.detail.eased,e)};return this.target().off("during.fx",i).on("during.fx",i),this.after((function(){this.off("during.fx",i)})),this._callStart()},afterAll:function(t){var e=function e(i){t.call(this),this.off("allfinished.fx",e)};return this.target().off("allfinished.fx",e).on("allfinished.fx",e),this._callStart()},last:function(){return this.situations.length?this.situations[this.situations.length-1]:this.situation},add:function(t,e,i){return this.last()[i||"animations"][t]=e,this._callStart()},step:function(t){var e,i,a;t||(this.absPos=this.timeToAbsPos(+new Date)),!1!==this.situation.loops?(e=Math.max(this.absPos,0),i=Math.floor(e),!0===this.situation.loops||i<this.situation.loops?(this.pos=e-i,a=this.situation.loop,this.situation.loop=i):(this.absPos=this.situation.loops,this.pos=1,a=this.situation.loop-1,this.situation.loop=this.situation.loops),this.situation.reversing&&(this.situation.reversed=this.situation.reversed!=Boolean((this.situation.loop-a)%2))):(this.absPos=Math.min(this.absPos,1),this.pos=this.absPos),this.pos<0&&(this.pos=0),this.situation.reversed&&(this.pos=1-this.pos);var s=this.situation.ease(this.pos);for(var r in this.situation.once)r>this.lastPos&&r<=s&&(this.situation.once[r].call(this.target(),this.pos,s),delete this.situation.once[r]);return this.active&&this.target().fire("during",{pos:this.pos,eased:s,fx:this,situation:this.situation}),this.situation?(this.eachAt(),1==this.pos&&!this.situation.reversed||this.situation.reversed&&0==this.pos?(this.stopAnimFrame(),this.target().fire("finished",{fx:this,situation:this.situation}),this.situations.length||(this.target().fire("allfinished"),this.situations.length||(this.target().off(".fx"),this.active=!1)),this.active?this.dequeue():this.clearCurrent()):!this.paused&&this.active&&this.startAnimFrame(),this.lastPos=s,this):this},eachAt:function(){var t,e=this,i=this.target(),s=this.situation;for(var r in s.animations)t=[].concat(s.animations[r]).map((function(t){return"string"!=typeof t&&t.at?t.at(s.ease(e.pos),e.pos):t})),i[r].apply(i,t);for(var r in s.attrs)t=[r].concat(s.attrs[r]).map((function(t){return"string"!=typeof t&&t.at?t.at(s.ease(e.pos),e.pos):t})),i.attr.apply(i,t);for(var r in s.styles)t=[r].concat(s.styles[r]).map((function(t){return"string"!=typeof t&&t.at?t.at(s.ease(e.pos),e.pos):t})),i.style.apply(i,t);if(s.transforms.length){t=s.initialTransformation,r=0;for(var n=s.transforms.length;r<n;r++){var o=s.transforms[r];o instanceof a.Matrix?t=o.relative?t.multiply((new a.Matrix).morph(o).at(s.ease(this.pos))):t.morph(o).at(s.ease(this.pos)):(o.relative||o.undo(t.extract()),t=t.multiply(o.at(s.ease(this.pos))))}i.matrix(t)}return this},once:function(t,e,i){var a=this.last();return i||(t=a.ease(t)),a.once[t]=e,this},_callStart:function(){return setTimeout(function(){this.start()}.bind(this),0),this}},parent:a.Element,construct:{animate:function(t,e,i){return(this.fx||(this.fx=new a.FX(this))).animate(t,e,i)},delay:function(t){return(this.fx||(this.fx=new a.FX(this))).delay(t)},stop:function(t,e){return this.fx&&this.fx.stop(t,e),this},finish:function(){return this.fx&&this.fx.finish(),this}}}),a.MorphObj=a.invent({create:function(t,e){return a.Color.isColor(e)?new a.Color(t).morph(e):a.regex.delimiter.test(t)?a.regex.pathLetters.test(t)?new a.PathArray(t).morph(e):new a.Array(t).morph(e):a.regex.numberAndUnit.test(e)?new a.Number(t).morph(e):(this.value=t,void(this.destination=e))},extend:{at:function(t,e){return e<1?this.value:this.destination},valueOf:function(){return this.value}}}),a.extend(a.FX,{attr:function(e,i,a){if("object"===t(e))for(var s in e)this.attr(s,e[s]);else this.add(e,i,"attrs");return this},plot:function(t,e,i,a){return 4==arguments.length?this.plot([t,e,i,a]):this.add("plot",new(this.target().morphArray)(t))}}),a.Box=a.invent({create:function(e,i,s,r){if(!("object"!==t(e)||e instanceof a.Element))return a.Box.call(this,null!=e.left?e.left:e.x,null!=e.top?e.top:e.y,e.width,e.height);4==arguments.length&&(this.x=e,this.y=i,this.width=s,this.height=r),b(this)}}),a.BBox=a.invent({create:function(t){if(a.Box.apply(this,[].slice.call(arguments)),t instanceof a.Element){var e;try{if(!i.documentElement.contains){for(var s=t.node;s.parentNode;)s=s.parentNode;if(s!=i)throw new Error("Element not in the dom")}e=t.node.getBBox()}catch(i){if(t instanceof a.Shape){a.parser.draw||a.prepare();var r=t.clone(a.parser.draw.instance).show();e=r.node.getBBox(),r.remove()}else e={x:t.node.clientLeft,y:t.node.clientTop,width:t.node.clientWidth,height:t.node.clientHeight}}a.Box.call(this,e)}},inherit:a.Box,parent:a.Element,construct:{bbox:function(){return new a.BBox(this)}}}),a.BBox.prototype.constructor=a.BBox,a.Matrix=a.invent({create:function(e){var i=p([1,0,0,1,0,0]);e=e instanceof a.Element?e.matrixify():"string"==typeof e?p(e.split(a.regex.delimiter).map(parseFloat)):6==arguments.length?p([].slice.call(arguments)):Array.isArray(e)?p(e):"object"===t(e)?e:i;for(var s=v.length-1;s>=0;--s)this[v[s]]=null!=e[v[s]]?e[v[s]]:i[v[s]]},extend:{extract:function(){var t=f(this,0,1),e=(f(this,1,0),180/Math.PI*Math.atan2(t.y,t.x)-90);return{x:this.e,y:this.f,transformedX:(this.e*Math.cos(e*Math.PI/180)+this.f*Math.sin(e*Math.PI/180))/Math.sqrt(this.a*this.a+this.b*this.b),transformedY:(this.f*Math.cos(e*Math.PI/180)+this.e*Math.sin(-e*Math.PI/180))/Math.sqrt(this.c*this.c+this.d*this.d),rotation:e,a:this.a,b:this.b,c:this.c,d:this.d,e:this.e,f:this.f,matrix:new a.Matrix(this)}},clone:function(){return new a.Matrix(this)},morph:function(t){return this.destination=new a.Matrix(t),this},multiply:function(t){return new a.Matrix(this.native().multiply(function(t){return t instanceof a.Matrix||(t=new a.Matrix(t)),t}(t).native()))},inverse:function(){return new a.Matrix(this.native().inverse())},translate:function(t,e){return new a.Matrix(this.native().translate(t||0,e||0))},native:function(){for(var t=a.parser.native.createSVGMatrix(),e=v.length-1;e>=0;e--)t[v[e]]=this[v[e]];return t},toString:function(){return"matrix("+m(this.a)+","+m(this.b)+","+m(this.c)+","+m(this.d)+","+m(this.e)+","+m(this.f)+")"}},parent:a.Element,construct:{ctm:function(){return new a.Matrix(this.node.getCTM())},screenCTM:function(){if(this instanceof a.Nested){var t=this.rect(1,1),e=t.node.getScreenCTM();return t.remove(),new a.Matrix(e)}return new a.Matrix(this.node.getScreenCTM())}}}),a.Point=a.invent({create:function(e,i){var a;a=Array.isArray(e)?{x:e[0],y:e[1]}:"object"===t(e)?{x:e.x,y:e.y}:null!=e?{x:e,y:null!=i?i:e}:{x:0,y:0},this.x=a.x,this.y=a.y},extend:{clone:function(){return new a.Point(this)},morph:function(t,e){return this.destination=new a.Point(t,e),this}}}),a.extend(a.Element,{point:function(t,e){return new a.Point(t,e).transform(this.screenCTM().inverse())}}),a.extend(a.Element,{attr:function(e,i,s){if(null==e){for(e={},s=(i=this.node.attributes).length-1;s>=0;s--)e[i[s].nodeName]=a.regex.isNumber.test(i[s].nodeValue)?parseFloat(i[s].nodeValue):i[s].nodeValue;return e}if("object"===t(e))for(var r in e)this.attr(r,e[r]);else if(null===i)this.node.removeAttribute(e);else{if(null==i)return null==(i=this.node.getAttribute(e))?a.defaults.attrs[e]:a.regex.isNumber.test(i)?parseFloat(i):i;"stroke-width"==e?this.attr("stroke",parseFloat(i)>0?this._stroke:null):"stroke"==e&&(this._stroke=i),"fill"!=e&&"stroke"!=e||(a.regex.isImage.test(i)&&(i=this.doc().defs().image(i,0,0)),i instanceof a.Image&&(i=this.doc().defs().pattern(0,0,(function(){this.add(i)})))),"number"==typeof i?i=new a.Number(i):a.Color.isColor(i)?i=new a.Color(i):Array.isArray(i)&&(i=new a.Array(i)),"leading"==e?this.leading&&this.leading(i):"string"==typeof s?this.node.setAttributeNS(s,e,i.toString()):this.node.setAttribute(e,i.toString()),!this.rebuild||"font-size"!=e&&"x"!=e||this.rebuild(e,i)}return this}}),a.extend(a.Element,{transform:function(e,i){var s;return"object"!==t(e)?(s=new a.Matrix(this).extract(),"string"==typeof e?s[e]:s):(s=new a.Matrix(this),i=!!i||!!e.relative,null!=e.a&&(s=i?s.multiply(new a.Matrix(e)):new a.Matrix(e)),this.attr("transform",s))}}),a.extend(a.Element,{untransform:function(){return this.attr("transform",null)},matrixify:function(){return(this.attr("transform")||"").split(a.regex.transforms).slice(0,-1).map((function(t){var e=t.trim().split("(");return[e[0],e[1].split(a.regex.delimiter).map((function(t){return parseFloat(t)}))]})).reduce((function(t,e){return"matrix"==e[0]?t.multiply(p(e[1])):t[e[0]].apply(t,e[1])}),new a.Matrix)},toParent:function(t){if(this==t)return this;var e=this.screenCTM(),i=t.screenCTM().inverse();return this.addTo(t).untransform().transform(i.multiply(e)),this},toDoc:function(){return this.toParent(this.doc())}}),a.Transformation=a.invent({create:function(e,i){if(arguments.length>1&&"boolean"!=typeof i)return this.constructor.call(this,[].slice.call(arguments));if(Array.isArray(e))for(var a=0,s=this.arguments.length;a<s;++a)this[this.arguments[a]]=e[a];else if("object"===t(e))for(a=0,s=this.arguments.length;a<s;++a)this[this.arguments[a]]=e[this.arguments[a]];this.inversed=!1,!0===i&&(this.inversed=!0)}}),a.Translate=a.invent({parent:a.Matrix,inherit:a.Transformation,create:function(t,e){this.constructor.apply(this,[].slice.call(arguments))},extend:{arguments:["transformedX","transformedY"],method:"translate"}}),a.extend(a.Element,{style:function(e,i){if(0==arguments.length)return this.node.style.cssText||"";if(arguments.length<2)if("object"===t(e))for(var s in e)this.style(s,e[s]);else{if(!a.regex.isCss.test(e))return this.node.style[c(e)];for(e=e.split(/\s*;\s*/).filter((function(t){return!!t})).map((function(t){return t.split(/\s*:\s*/)}));i=e.pop();)this.style(i[0],i[1])}else this.node.style[c(e)]=null===i||a.regex.isBlank.test(i)?"":i;return this}}),a.Parent=a.invent({create:function(t){this.constructor.call(this,t)},inherit:a.Element,extend:{children:function(){return a.utils.map(a.utils.filterSVGElements(this.node.childNodes),(function(t){return a.adopt(t)}))},add:function(t,e){return null==e?this.node.appendChild(t.node):t.node!=this.node.childNodes[e]&&this.node.insertBefore(t.node,this.node.childNodes[e]),this},put:function(t,e){return this.add(t,e),t},has:function(t){return this.index(t)>=0},index:function(t){return[].slice.call(this.node.childNodes).indexOf(t.node)},get:function(t){return a.adopt(this.node.childNodes[t])},first:function(){return this.get(0)},last:function(){return this.get(this.node.childNodes.length-1)},each:function(t,e){for(var i=this.children(),s=0,r=i.length;s<r;s++)i[s]instanceof a.Element&&t.apply(i[s],[s,i]),e&&i[s]instanceof a.Container&&i[s].each(t,e);return this},removeElement:function(t){return this.node.removeChild(t.node),this},clear:function(){for(;this.node.hasChildNodes();)this.node.removeChild(this.node.lastChild);return delete this._defs,this},defs:function(){return this.doc().defs()}}}),a.extend(a.Parent,{ungroup:function(t,e){return 0===e||this instanceof a.Defs||this.node==a.parser.draw||(t=t||(this instanceof a.Doc?this:this.parent(a.Parent)),e=e||1/0,this.each((function(){return this instanceof a.Defs?this:this instanceof a.Parent?this.ungroup(t,e-1):this.toParent(t)})),this.node.firstChild||this.remove()),this},flatten:function(t,e){return this.ungroup(t,e)}}),a.Container=a.invent({create:function(t){this.constructor.call(this,t)},inherit:a.Parent}),a.ViewBox=a.invent({parent:a.Container,construct:{}}),["click","dblclick","mousedown","mouseup","mouseover","mouseout","mousemove","touchstart","touchmove","touchleave","touchend","touchcancel"].forEach((function(t){a.Element.prototype[t]=function(e){return a.on(this.node,t,e),this}})),a.listeners=[],a.handlerMap=[],a.listenerId=0,a.on=function(t,e,i,s,r){var n=i.bind(s||t.instance||t),o=(a.handlerMap.indexOf(t)+1||a.handlerMap.push(t))-1,l=e.split(".")[0],h=e.split(".")[1]||"*";a.listeners[o]=a.listeners[o]||{},a.listeners[o][l]=a.listeners[o][l]||{},a.listeners[o][l][h]=a.listeners[o][l][h]||{},i._svgjsListenerId||(i._svgjsListenerId=++a.listenerId),a.listeners[o][l][h][i._svgjsListenerId]=n,t.addEventListener(l,n,r||!1)},a.off=function(t,e,i){var s=a.handlerMap.indexOf(t),r=e&&e.split(".")[0],n=e&&e.split(".")[1],o="";if(-1!=s)if(i){if("function"==typeof i&&(i=i._svgjsListenerId),!i)return;a.listeners[s][r]&&a.listeners[s][r][n||"*"]&&(t.removeEventListener(r,a.listeners[s][r][n||"*"][i],!1),delete a.listeners[s][r][n||"*"][i])}else if(n&&r){if(a.listeners[s][r]&&a.listeners[s][r][n]){for(var l in a.listeners[s][r][n])a.off(t,[r,n].join("."),l);delete a.listeners[s][r][n]}}else if(n)for(var h in a.listeners[s])for(var o in a.listeners[s][h])n===o&&a.off(t,[h,n].join("."));else if(r){if(a.listeners[s][r]){for(var o in a.listeners[s][r])a.off(t,[r,o].join("."));delete a.listeners[s][r]}}else{for(var h in a.listeners[s])a.off(t,h);delete a.listeners[s],delete a.handlerMap[s]}},a.extend(a.Element,{on:function(t,e,i,s){return a.on(this.node,t,e,i,s),this},off:function(t,e){return a.off(this.node,t,e),this},fire:function(t,i){return t instanceof e.Event?this.node.dispatchEvent(t):this.node.dispatchEvent(t=new a.CustomEvent(t,{detail:i,cancelable:!0})),this._event=t,this},event:function(){return this._event}}),a.Defs=a.invent({create:"defs",inherit:a.Container}),a.G=a.invent({create:"g",inherit:a.Container,extend:{x:function(t){return null==t?this.transform("x"):this.transform({x:t-this.x()},!0)}},construct:{group:function(){return this.put(new a.G)}}}),a.Doc=a.invent({create:function(t){t&&("svg"==(t="string"==typeof t?i.getElementById(t):t).nodeName?this.constructor.call(this,t):(this.constructor.call(this,a.create("svg")),t.appendChild(this.node),this.size("100%","100%")),this.namespace().defs())},inherit:a.Container,extend:{namespace:function(){return this.attr({xmlns:a.ns,version:"1.1"}).attr("xmlns:xlink",a.xlink,a.xmlns).attr("xmlns:svgjs",a.svgjs,a.xmlns)},defs:function(){var t;return this._defs||((t=this.node.getElementsByTagName("defs")[0])?this._defs=a.adopt(t):this._defs=new a.Defs,this.node.appendChild(this._defs.node)),this._defs},parent:function(){return this.node.parentNode&&"#document"!=this.node.parentNode.nodeName?this.node.parentNode:null},remove:function(){return this.parent()&&this.parent().removeChild(this.node),this},clear:function(){for(;this.node.hasChildNodes();)this.node.removeChild(this.node.lastChild);return delete this._defs,a.parser.draw&&!a.parser.draw.parentNode&&this.node.appendChild(a.parser.draw),this},clone:function(t){this.writeDataToDom();var e=this.node,i=x(e.cloneNode(!0));return t?(t.node||t).appendChild(i.node):e.parentNode.insertBefore(i.node,e.nextSibling),i}}}),a.extend(a.Element,{}),a.Gradient=a.invent({create:function(t){this.constructor.call(this,a.create(t+"Gradient")),this.type=t},inherit:a.Container,extend:{at:function(t,e,i){return this.put(new a.Stop).update(t,e,i)},update:function(t){return this.clear(),"function"==typeof t&&t.call(this,this),this},fill:function(){return"url(#"+this.id()+")"},toString:function(){return this.fill()},attr:function(t,e,i){return"transform"==t&&(t="gradientTransform"),a.Container.prototype.attr.call(this,t,e,i)}},construct:{gradient:function(t,e){return this.defs().gradient(t,e)}}}),a.extend(a.Gradient,a.FX,{from:function(t,e){return"radial"==(this._target||this).type?this.attr({fx:new a.Number(t),fy:new a.Number(e)}):this.attr({x1:new a.Number(t),y1:new a.Number(e)})},to:function(t,e){return"radial"==(this._target||this).type?this.attr({cx:new a.Number(t),cy:new a.Number(e)}):this.attr({x2:new a.Number(t),y2:new a.Number(e)})}}),a.extend(a.Defs,{gradient:function(t,e){return this.put(new a.Gradient(t)).update(e)}}),a.Stop=a.invent({create:"stop",inherit:a.Element,extend:{update:function(t){return("number"==typeof t||t instanceof a.Number)&&(t={offset:arguments[0],color:arguments[1],opacity:arguments[2]}),null!=t.opacity&&this.attr("stop-opacity",t.opacity),null!=t.color&&this.attr("stop-color",t.color),null!=t.offset&&this.attr("offset",new a.Number(t.offset)),this}}}),a.Pattern=a.invent({create:"pattern",inherit:a.Container,extend:{fill:function(){return"url(#"+this.id()+")"},update:function(t){return this.clear(),"function"==typeof t&&t.call(this,this),this},toString:function(){return this.fill()},attr:function(t,e,i){return"transform"==t&&(t="patternTransform"),a.Container.prototype.attr.call(this,t,e,i)}},construct:{pattern:function(t,e,i){return this.defs().pattern(t,e,i)}}}),a.extend(a.Defs,{pattern:function(t,e,i){return this.put(new a.Pattern).update(i).attr({x:0,y:0,width:t,height:e,patternUnits:"userSpaceOnUse"})}}),a.Shape=a.invent({create:function(t){this.constructor.call(this,t)},inherit:a.Element}),a.Symbol=a.invent({create:"symbol",inherit:a.Container,construct:{symbol:function(){return this.put(new a.Symbol)}}}),a.Use=a.invent({create:"use",inherit:a.Shape,extend:{element:function(t,e){return this.attr("href",(e||"")+"#"+t,a.xlink)}},construct:{use:function(t,e){return this.put(new a.Use).element(t,e)}}}),a.Rect=a.invent({create:"rect",inherit:a.Shape,construct:{rect:function(t,e){return this.put(new a.Rect).size(t,e)}}}),a.Circle=a.invent({create:"circle",inherit:a.Shape,construct:{circle:function(t){return this.put(new a.Circle).rx(new a.Number(t).divide(2)).move(0,0)}}}),a.extend(a.Circle,a.FX,{rx:function(t){return this.attr("r",t)},ry:function(t){return this.rx(t)}}),a.Ellipse=a.invent({create:"ellipse",inherit:a.Shape,construct:{ellipse:function(t,e){return this.put(new a.Ellipse).size(t,e).move(0,0)}}}),a.extend(a.Ellipse,a.Rect,a.FX,{rx:function(t){return this.attr("rx",t)},ry:function(t){return this.attr("ry",t)}}),a.extend(a.Circle,a.Ellipse,{x:function(t){return null==t?this.cx()-this.rx():this.cx(t+this.rx())},y:function(t){return null==t?this.cy()-this.ry():this.cy(t+this.ry())},cx:function(t){return null==t?this.attr("cx"):this.attr("cx",t)},cy:function(t){return null==t?this.attr("cy"):this.attr("cy",t)},width:function(t){return null==t?2*this.rx():this.rx(new a.Number(t).divide(2))},height:function(t){return null==t?2*this.ry():this.ry(new a.Number(t).divide(2))},size:function(t,e){var i=u(this,t,e);return this.rx(new a.Number(i.width).divide(2)).ry(new a.Number(i.height).divide(2))}}),a.Line=a.invent({create:"line",inherit:a.Shape,extend:{array:function(){return new a.PointArray([[this.attr("x1"),this.attr("y1")],[this.attr("x2"),this.attr("y2")]])},plot:function(t,e,i,s){return null==t?this.array():(t=void 0!==e?{x1:t,y1:e,x2:i,y2:s}:new a.PointArray(t).toLine(),this.attr(t))},move:function(t,e){return this.attr(this.array().move(t,e).toLine())},size:function(t,e){var i=u(this,t,e);return this.attr(this.array().size(i.width,i.height).toLine())}},construct:{line:function(t,e,i,s){return a.Line.prototype.plot.apply(this.put(new a.Line),null!=t?[t,e,i,s]:[0,0,0,0])}}}),a.Polyline=a.invent({create:"polyline",inherit:a.Shape,construct:{polyline:function(t){return this.put(new a.Polyline).plot(t||new a.PointArray)}}}),a.Polygon=a.invent({create:"polygon",inherit:a.Shape,construct:{polygon:function(t){return this.put(new a.Polygon).plot(t||new a.PointArray)}}}),a.extend(a.Polyline,a.Polygon,{array:function(){return this._array||(this._array=new a.PointArray(this.attr("points")))},plot:function(t){return null==t?this.array():this.clear().attr("points","string"==typeof t?t:this._array=new a.PointArray(t))},clear:function(){return delete this._array,this},move:function(t,e){return this.attr("points",this.array().move(t,e))},size:function(t,e){var i=u(this,t,e);return this.attr("points",this.array().size(i.width,i.height))}}),a.extend(a.Line,a.Polyline,a.Polygon,{morphArray:a.PointArray,x:function(t){return null==t?this.bbox().x:this.move(t,this.bbox().y)},y:function(t){return null==t?this.bbox().y:this.move(this.bbox().x,t)},width:function(t){var e=this.bbox();return null==t?e.width:this.size(t,e.height)},height:function(t){var e=this.bbox();return null==t?e.height:this.size(e.width,t)}}),a.Path=a.invent({create:"path",inherit:a.Shape,extend:{morphArray:a.PathArray,array:function(){return this._array||(this._array=new a.PathArray(this.attr("d")))},plot:function(t){return null==t?this.array():this.clear().attr("d","string"==typeof t?t:this._array=new a.PathArray(t))},clear:function(){return delete this._array,this}},construct:{path:function(t){return this.put(new a.Path).plot(t||new a.PathArray)}}}),a.Image=a.invent({create:"image",inherit:a.Shape,extend:{load:function(t){if(!t)return this;var i=this,s=new e.Image;return a.on(s,"load",(function(){a.off(s);var e=i.parent(a.Pattern);null!==e&&(0==i.width()&&0==i.height()&&i.size(s.width,s.height),e&&0==e.width()&&0==e.height()&&e.size(i.width(),i.height()),"function"==typeof i._loaded&&i._loaded.call(i,{width:s.width,height:s.height,ratio:s.width/s.height,url:t}))})),a.on(s,"error",(function(t){a.off(s),"function"==typeof i._error&&i._error.call(i,t)})),this.attr("href",s.src=this.src=t,a.xlink)},loaded:function(t){return this._loaded=t,this},error:function(t){return this._error=t,this}},construct:{image:function(t,e,i){return this.put(new a.Image).load(t).size(e||0,i||e||0)}}}),a.Text=a.invent({create:function(){this.constructor.call(this,a.create("text")),this.dom.leading=new a.Number(1.3),this._rebuild=!0,this._build=!1,this.attr("font-family",a.defaults.attrs["font-family"])},inherit:a.Shape,extend:{x:function(t){return null==t?this.attr("x"):this.attr("x",t)},text:function(t){if(void 0===t){t="";for(var e=this.node.childNodes,i=0,s=e.length;i<s;++i)0!=i&&3!=e[i].nodeType&&1==a.adopt(e[i]).dom.newLined&&(t+="\n"),t+=e[i].textContent;return t}if(this.clear().build(!0),"function"==typeof t)t.call(this,this);else{i=0;for(var r=(t=t.split("\n")).length;i<r;i++)this.tspan(t[i]).newLine()}return this.build(!1).rebuild()},size:function(t){return this.attr("font-size",t).rebuild()},leading:function(t){return null==t?this.dom.leading:(this.dom.leading=new a.Number(t),this.rebuild())},lines:function(){var t=(this.textPath&&this.textPath()||this).node,e=a.utils.map(a.utils.filterSVGElements(t.childNodes),(function(t){return a.adopt(t)}));return new a.Set(e)},rebuild:function(t){if("boolean"==typeof t&&(this._rebuild=t),this._rebuild){var e=this,i=0,s=this.dom.leading*new a.Number(this.attr("font-size"));this.lines().each((function(){this.dom.newLined&&(e.textPath()||this.attr("x",e.attr("x")),"\n"==this.text()?i+=s:(this.attr("dy",s+i),i=0))})),this.fire("rebuild")}return this},build:function(t){return this._build=!!t,this},setData:function(t){return this.dom=t,this.dom.leading=new a.Number(t.leading||1.3),this}},construct:{text:function(t){return this.put(new a.Text).text(t)},plain:function(t){return this.put(new a.Text).plain(t)}}}),a.Tspan=a.invent({create:"tspan",inherit:a.Shape,extend:{text:function(t){return null==t?this.node.textContent+(this.dom.newLined?"\n":""):("function"==typeof t?t.call(this,this):this.plain(t),this)},dx:function(t){return this.attr("dx",t)},dy:function(t){return this.attr("dy",t)},newLine:function(){var t=this.parent(a.Text);return this.dom.newLined=!0,this.dy(t.dom.leading*t.attr("font-size")).attr("x",t.x())}}}),a.extend(a.Text,a.Tspan,{plain:function(t){return!1===this._build&&this.clear(),this.node.appendChild(i.createTextNode(t)),this},tspan:function(t){var e=(this.textPath&&this.textPath()||this).node,i=new a.Tspan;return!1===this._build&&this.clear(),e.appendChild(i.node),i.text(t)},clear:function(){for(var t=(this.textPath&&this.textPath()||this).node;t.hasChildNodes();)t.removeChild(t.lastChild);return this},length:function(){return this.node.getComputedTextLength()}}),a.TextPath=a.invent({create:"textPath",inherit:a.Parent,parent:a.Text,construct:{morphArray:a.PathArray,array:function(){var t=this.track();return t?t.array():null},plot:function(t){var e=this.track(),i=null;return e&&(i=e.plot(t)),null==t?i:this},track:function(){var t=this.textPath();if(t)return t.reference("href")},textPath:function(){if(this.node.firstChild&&"textPath"==this.node.firstChild.nodeName)return a.adopt(this.node.firstChild)}}}),a.Nested=a.invent({create:function(){this.constructor.call(this,a.create("svg")),this.style("overflow","visible")},inherit:a.Container,construct:{nested:function(){return this.put(new a.Nested)}}});var l={stroke:["color","width","opacity","linecap","linejoin","miterlimit","dasharray","dashoffset"],fill:["color","opacity","rule"],prefix:function(t,e){return"color"==e?t:t+"-"+e}};function h(t,e,i,s){return i+s.replace(a.regex.dots," .")}function c(t){return t.toLowerCase().replace(/-(.)/g,(function(t,e){return e.toUpperCase()}))}function d(t){return t.charAt(0).toUpperCase()+t.slice(1)}function g(t){var e=t.toString(16);return 1==e.length?"0"+e:e}function u(t,e,i){if(null==e||null==i){var a=t.bbox();null==e?e=a.width/a.height*i:null==i&&(i=a.height/a.width*e)}return{width:e,height:i}}function f(t,e,i){return{x:e*t.a+i*t.c+0,y:e*t.b+i*t.d+0}}function p(t){return{a:t[0],b:t[1],c:t[2],d:t[3],e:t[4],f:t[5]}}function x(t){for(var i=t.childNodes.length-1;i>=0;i--)t.childNodes[i]instanceof e.SVGElement&&x(t.childNodes[i]);return a.adopt(t).id(a.eid(t.nodeName))}function b(t){return null==t.x&&(t.x=0,t.y=0,t.width=0,t.height=0),t.w=t.width,t.h=t.height,t.x2=t.x+t.width,t.y2=t.y+t.height,t.cx=t.x+t.width/2,t.cy=t.y+t.height/2,t}function m(t){return Math.abs(t)>1e-37?t:0}["fill","stroke"].forEach((function(t){var e={};e[t]=function(e){if(void 0===e)return this;if("string"==typeof e||a.Color.isRgb(e)||e&&"function"==typeof e.fill)this.attr(t,e);else for(var i=l[t].length-1;i>=0;i--)null!=e[l[t][i]]&&this.attr(l.prefix(t,l[t][i]),e[l[t][i]]);return this},a.extend(a.Element,a.FX,e)})),a.extend(a.Element,a.FX,{translate:function(t,e){return this.transform({x:t,y:e})},matrix:function(t){return this.attr("transform",new a.Matrix(6==arguments.length?[].slice.call(arguments):t))},opacity:function(t){return this.attr("opacity",t)},dx:function(t){return this.x(new a.Number(t).plus(this instanceof a.FX?0:this.x()),!0)},dy:function(t){return this.y(new a.Number(t).plus(this instanceof a.FX?0:this.y()),!0)}}),a.extend(a.Path,{length:function(){return this.node.getTotalLength()},pointAt:function(t){return this.node.getPointAtLength(t)}}),a.Set=a.invent({create:function(t){Array.isArray(t)?this.members=t:this.clear()},extend:{add:function(){for(var t=[].slice.call(arguments),e=0,i=t.length;e<i;e++)this.members.push(t[e]);return this},remove:function(t){var e=this.index(t);return e>-1&&this.members.splice(e,1),this},each:function(t){for(var e=0,i=this.members.length;e<i;e++)t.apply(this.members[e],[e,this.members]);return this},clear:function(){return this.members=[],this},length:function(){return this.members.length},has:function(t){return this.index(t)>=0},index:function(t){return this.members.indexOf(t)},get:function(t){return this.members[t]},first:function(){return this.get(0)},last:function(){return this.get(this.members.length-1)},valueOf:function(){return this.members}},construct:{set:function(t){return new a.Set(t)}}}),a.FX.Set=a.invent({create:function(t){this.set=t}}),a.Set.inherit=function(){var t=[];for(var e in a.Shape.prototype)"function"==typeof a.Shape.prototype[e]&&"function"!=typeof a.Set.prototype[e]&&t.push(e);for(var e in t.forEach((function(t){a.Set.prototype[t]=function(){for(var e=0,i=this.members.length;e<i;e++)this.members[e]&&"function"==typeof this.members[e][t]&&this.members[e][t].apply(this.members[e],arguments);return"animate"==t?this.fx||(this.fx=new a.FX.Set(this)):this}})),t=[],a.FX.prototype)"function"==typeof a.FX.prototype[e]&&"function"!=typeof a.FX.Set.prototype[e]&&t.push(e);t.forEach((function(t){a.FX.Set.prototype[t]=function(){for(var e=0,i=this.set.members.length;e<i;e++)this.set.members[e].fx[t].apply(this.set.members[e].fx,arguments);return this}}))},a.extend(a.Element,{}),a.extend(a.Element,{remember:function(e,i){if("object"===t(arguments[0]))for(var a in e)this.remember(a,e[a]);else{if(1==arguments.length)return this.memory()[e];this.memory()[e]=i}return this},forget:function(){if(0==arguments.length)this._memory={};else for(var t=arguments.length-1;t>=0;t--)delete this.memory()[arguments[t]];return this},memory:function(){return this._memory||(this._memory={})}}),a.get=function(t){var e=i.getElementById(function(t){var e=(t||"").toString().match(a.regex.reference);if(e)return e[1]}(t)||t);return a.adopt(e)},a.select=function(t,e){return new a.Set(a.utils.map((e||i).querySelectorAll(t),(function(t){return a.adopt(t)})))},a.extend(a.Parent,{select:function(t){return a.select(t,this.node)}});var v="abcdef".split("");if("function"!=typeof e.CustomEvent){var y=function(t,e){e=e||{bubbles:!1,cancelable:!1,detail:void 0};var a=i.createEvent("CustomEvent");return a.initCustomEvent(t,e.bubbles,e.cancelable,e.detail),a};y.prototype=e.Event.prototype,a.CustomEvent=y}else a.CustomEvent=e.CustomEvent;return a},"function"==typeof define&&define.amd?define((function(){return S(A,A.document)})):"object"===("undefined"==typeof exports?"undefined":t(exports))&&"undefined"!=typeof module?module.exports=A.document?S(A,A.document):function(t){return S(t,t.document)}:A.SVG=S(A,A.document),
|
7 |
+
/*! svg.filter.js - v2.0.2 - 2016-02-24
|
8 |
+
* https://github.com/wout/svg.filter.js
|
9 |
+
* Copyright (c) 2016 Wout Fierens; Licensed MIT */
|
10 |
+
function(){SVG.Filter=SVG.invent({create:"filter",inherit:SVG.Parent,extend:{source:"SourceGraphic",sourceAlpha:"SourceAlpha",background:"BackgroundImage",backgroundAlpha:"BackgroundAlpha",fill:"FillPaint",stroke:"StrokePaint",autoSetIn:!0,put:function(t,e){return this.add(t,e),!t.attr("in")&&this.autoSetIn&&t.attr("in",this.source),t.attr("result")||t.attr("result",t),t},blend:function(t,e,i){return this.put(new SVG.BlendEffect(t,e,i))},colorMatrix:function(t,e){return this.put(new SVG.ColorMatrixEffect(t,e))},convolveMatrix:function(t){return this.put(new SVG.ConvolveMatrixEffect(t))},componentTransfer:function(t){return this.put(new SVG.ComponentTransferEffect(t))},composite:function(t,e,i){return this.put(new SVG.CompositeEffect(t,e,i))},flood:function(t,e){return this.put(new SVG.FloodEffect(t,e))},offset:function(t,e){return this.put(new SVG.OffsetEffect(t,e))},image:function(t){return this.put(new SVG.ImageEffect(t))},merge:function(){var t=[void 0];for(var e in arguments)t.push(arguments[e]);return this.put(new(SVG.MergeEffect.bind.apply(SVG.MergeEffect,t)))},gaussianBlur:function(t,e){return this.put(new SVG.GaussianBlurEffect(t,e))},morphology:function(t,e){return this.put(new SVG.MorphologyEffect(t,e))},diffuseLighting:function(t,e,i){return this.put(new SVG.DiffuseLightingEffect(t,e,i))},displacementMap:function(t,e,i,a,s){return this.put(new SVG.DisplacementMapEffect(t,e,i,a,s))},specularLighting:function(t,e,i,a){return this.put(new SVG.SpecularLightingEffect(t,e,i,a))},tile:function(){return this.put(new SVG.TileEffect)},turbulence:function(t,e,i,a,s){return this.put(new SVG.TurbulenceEffect(t,e,i,a,s))},toString:function(){return"url(#"+this.attr("id")+")"}}}),SVG.extend(SVG.Defs,{filter:function(t){var e=this.put(new SVG.Filter);return"function"==typeof t&&t.call(e,e),e}}),SVG.extend(SVG.Container,{filter:function(t){return this.defs().filter(t)}}),SVG.extend(SVG.Element,SVG.G,SVG.Nested,{filter:function(t){return this.filterer=t instanceof SVG.Element?t:this.doc().filter(t),this.doc()&&this.filterer.doc()!==this.doc()&&this.doc().defs().add(this.filterer),this.attr("filter",this.filterer),this.filterer},unfilter:function(t){return this.filterer&&!0===t&&this.filterer.remove(),delete this.filterer,this.attr("filter",null)}}),SVG.Effect=SVG.invent({create:function(){this.constructor.call(this)},inherit:SVG.Element,extend:{in:function(t){return null==t?this.parent()&&this.parent().select('[result="'+this.attr("in")+'"]').get(0)||this.attr("in"):this.attr("in",t)},result:function(t){return null==t?this.attr("result"):this.attr("result",t)},toString:function(){return this.result()}}}),SVG.ParentEffect=SVG.invent({create:function(){this.constructor.call(this)},inherit:SVG.Parent,extend:{in:function(t){return null==t?this.parent()&&this.parent().select('[result="'+this.attr("in")+'"]').get(0)||this.attr("in"):this.attr("in",t)},result:function(t){return null==t?this.attr("result"):this.attr("result",t)},toString:function(){return this.result()}}});var t={blend:function(t,e){return this.parent()&&this.parent().blend(this,t,e)},colorMatrix:function(t,e){return this.parent()&&this.parent().colorMatrix(t,e).in(this)},convolveMatrix:function(t){return this.parent()&&this.parent().convolveMatrix(t).in(this)},componentTransfer:function(t){return this.parent()&&this.parent().componentTransfer(t).in(this)},composite:function(t,e){return this.parent()&&this.parent().composite(this,t,e)},flood:function(t,e){return this.parent()&&this.parent().flood(t,e)},offset:function(t,e){return this.parent()&&this.parent().offset(t,e).in(this)},image:function(t){return this.parent()&&this.parent().image(t)},merge:function(){return this.parent()&&this.parent().merge.apply(this.parent(),[this].concat(arguments))},gaussianBlur:function(t,e){return this.parent()&&this.parent().gaussianBlur(t,e).in(this)},morphology:function(t,e){return this.parent()&&this.parent().morphology(t,e).in(this)},diffuseLighting:function(t,e,i){return this.parent()&&this.parent().diffuseLighting(t,e,i).in(this)},displacementMap:function(t,e,i,a){return this.parent()&&this.parent().displacementMap(this,t,e,i,a)},specularLighting:function(t,e,i,a){return this.parent()&&this.parent().specularLighting(t,e,i,a).in(this)},tile:function(){return this.parent()&&this.parent().tile().in(this)},turbulence:function(t,e,i,a,s){return this.parent()&&this.parent().turbulence(t,e,i,a,s).in(this)}};SVG.extend(SVG.Effect,t),SVG.extend(SVG.ParentEffect,t),SVG.ChildEffect=SVG.invent({create:function(){this.constructor.call(this)},inherit:SVG.Element,extend:{in:function(t){this.attr("in",t)}}});var e={blend:function(t,e,i){this.attr({in:t,in2:e,mode:i||"normal"})},colorMatrix:function(t,e){"matrix"==t&&(e=s(e)),this.attr({type:t,values:void 0===e?null:e})},convolveMatrix:function(t){t=s(t),this.attr({order:Math.sqrt(t.split(" ").length),kernelMatrix:t})},composite:function(t,e,i){this.attr({in:t,in2:e,operator:i})},flood:function(t,e){this.attr("flood-color",t),null!=e&&this.attr("flood-opacity",e)},offset:function(t,e){this.attr({dx:t,dy:e})},image:function(t){this.attr("href",t,SVG.xlink)},displacementMap:function(t,e,i,a,s){this.attr({in:t,in2:e,scale:i,xChannelSelector:a,yChannelSelector:s})},gaussianBlur:function(t,e){null!=t||null!=e?this.attr("stdDeviation",r(Array.prototype.slice.call(arguments))):this.attr("stdDeviation","0 0")},morphology:function(t,e){this.attr({operator:t,radius:e})},tile:function(){},turbulence:function(t,e,i,a,s){this.attr({numOctaves:e,seed:i,stitchTiles:a,baseFrequency:t,type:s})}},i={merge:function(){var t;if(arguments[0]instanceof SVG.Set){var e=this;arguments[0].each((function(t){this instanceof SVG.MergeNode?e.put(this):(this instanceof SVG.Effect||this instanceof SVG.ParentEffect)&&e.put(new SVG.MergeNode(this))}))}else{t=Array.isArray(arguments[0])?arguments[0]:arguments;for(var i=0;i<t.length;i++)t[i]instanceof SVG.MergeNode?this.put(t[i]):this.put(new SVG.MergeNode(t[i]))}},componentTransfer:function(t){if(this.rgb=new SVG.Set,["r","g","b","a"].forEach(function(t){this[t]=new(SVG["Func"+t.toUpperCase()])("identity"),this.rgb.add(this[t]),this.node.appendChild(this[t].node)}.bind(this)),t)for(var e in t.rgb&&(["r","g","b"].forEach(function(e){this[e].attr(t.rgb)}.bind(this)),delete t.rgb),t)this[e].attr(t[e])},diffuseLighting:function(t,e,i){this.attr({surfaceScale:t,diffuseConstant:e,kernelUnitLength:i})},specularLighting:function(t,e,i,a){this.attr({surfaceScale:t,diffuseConstant:e,specularExponent:i,kernelUnitLength:a})}},a={distantLight:function(t,e){this.attr({azimuth:t,elevation:e})},pointLight:function(t,e,i){this.attr({x:t,y:e,z:i})},spotLight:function(t,e,i,a,s,r){this.attr({x:t,y:e,z:i,pointsAtX:a,pointsAtY:s,pointsAtZ:r})},mergeNode:function(t){this.attr("in",t)}};function s(t){return Array.isArray(t)&&(t=new SVG.Array(t)),t.toString().replace(/^\s+/,"").replace(/\s+$/,"").replace(/\s+/g," ")}function r(t){if(!Array.isArray(t))return t;for(var e=0,i=t.length,a=[];e<i;e++)a.push(t[e]);return a.join(" ")}function n(){var t=function(){};for(var e in"function"==typeof arguments[arguments.length-1]&&(t=arguments[arguments.length-1],Array.prototype.splice.call(arguments,arguments.length-1,1)),arguments)for(var i in arguments[e])t(arguments[e][i],i,arguments[e])}["r","g","b","a"].forEach((function(t){a["Func"+t.toUpperCase()]=function(t){switch(this.attr("type",t),t){case"table":this.attr("tableValues",arguments[1]);break;case"linear":this.attr("slope",arguments[1]),this.attr("intercept",arguments[2]);break;case"gamma":this.attr("amplitude",arguments[1]),this.attr("exponent",arguments[2]),this.attr("offset",arguments[2])}}})),n(e,(function(t,e){var i=e.charAt(0).toUpperCase()+e.slice(1);SVG[i+"Effect"]=SVG.invent({create:function(){this.constructor.call(this,SVG.create("fe"+i)),t.apply(this,arguments),this.result(this.attr("id")+"Out")},inherit:SVG.Effect,extend:{}})})),n(i,(function(t,e){var i=e.charAt(0).toUpperCase()+e.slice(1);SVG[i+"Effect"]=SVG.invent({create:function(){this.constructor.call(this,SVG.create("fe"+i)),t.apply(this,arguments),this.result(this.attr("id")+"Out")},inherit:SVG.ParentEffect,extend:{}})})),n(a,(function(t,e){var i=e.charAt(0).toUpperCase()+e.slice(1);SVG[i]=SVG.invent({create:function(){this.constructor.call(this,SVG.create("fe"+i)),t.apply(this,arguments)},inherit:SVG.ChildEffect,extend:{}})})),SVG.extend(SVG.MergeEffect,{in:function(t){return t instanceof SVG.MergeNode?this.add(t,0):this.add(new SVG.MergeNode(t),0),this}}),SVG.extend(SVG.CompositeEffect,SVG.BlendEffect,SVG.DisplacementMapEffect,{in2:function(t){return null==t?this.parent()&&this.parent().select('[result="'+this.attr("in2")+'"]').get(0)||this.attr("in2"):this.attr("in2",t)}}),SVG.filter={sepiatone:[.343,.669,.119,0,0,.249,.626,.13,0,0,.172,.334,.111,0,0,0,0,0,1,0]}}.call(void 0),function(){function t(t,s,r,n,o,l,h){for(var c=t.slice(s,r||h),d=n.slice(o,l||h),g=0,u={pos:[0,0],start:[0,0]},f={pos:[0,0],start:[0,0]};;){if(c[g]=e.call(u,c[g]),d[g]=e.call(f,d[g]),c[g][0]!=d[g][0]||"M"==c[g][0]||"A"==c[g][0]&&(c[g][4]!=d[g][4]||c[g][5]!=d[g][5])?(Array.prototype.splice.apply(c,[g,1].concat(a.call(u,c[g]))),Array.prototype.splice.apply(d,[g,1].concat(a.call(f,d[g])))):(c[g]=i.call(u,c[g]),d[g]=i.call(f,d[g])),++g==c.length&&g==d.length)break;g==c.length&&c.push(["C",u.pos[0],u.pos[1],u.pos[0],u.pos[1],u.pos[0],u.pos[1]]),g==d.length&&d.push(["C",f.pos[0],f.pos[1],f.pos[0],f.pos[1],f.pos[0],f.pos[1]])}return{start:c,dest:d}}function e(t){switch(t[0]){case"z":case"Z":t[0]="L",t[1]=this.start[0],t[2]=this.start[1];break;case"H":t[0]="L",t[2]=this.pos[1];break;case"V":t[0]="L",t[2]=t[1],t[1]=this.pos[0];break;case"T":t[0]="Q",t[3]=t[1],t[4]=t[2],t[1]=this.reflection[1],t[2]=this.reflection[0];break;case"S":t[0]="C",t[6]=t[4],t[5]=t[3],t[4]=t[2],t[3]=t[1],t[2]=this.reflection[1],t[1]=this.reflection[0]}return t}function i(t){var e=t.length;return this.pos=[t[e-2],t[e-1]],-1!="SCQT".indexOf(t[0])&&(this.reflection=[2*this.pos[0]-t[e-4],2*this.pos[1]-t[e-3]]),t}function a(t){var e=[t];switch(t[0]){case"M":return this.pos=this.start=[t[1],t[2]],e;case"L":t[5]=t[3]=t[1],t[6]=t[4]=t[2],t[1]=this.pos[0],t[2]=this.pos[1];break;case"Q":t[6]=t[4],t[5]=t[3],t[4]=1*t[4]/3+2*t[2]/3,t[3]=1*t[3]/3+2*t[1]/3,t[2]=1*this.pos[1]/3+2*t[2]/3,t[1]=1*this.pos[0]/3+2*t[1]/3;break;case"A":t=(e=function(t,e){var i,a,s,r,n,o,l,h,c,d,g,u,f,p,x,b,m,v,y,w,k,A,S,C,L,P,T=Math.abs(e[1]),z=Math.abs(e[2]),I=e[3]%360,M=e[4],E=e[5],X=e[6],Y=e[7],F=new SVG.Point(t),R=new SVG.Point(X,Y),D=[];if(0===T||0===z||F.x===R.x&&F.y===R.y)return[["C",F.x,F.y,R.x,R.y,R.x,R.y]];i=new SVG.Point((F.x-R.x)/2,(F.y-R.y)/2).transform((new SVG.Matrix).rotate(I)),(a=i.x*i.x/(T*T)+i.y*i.y/(z*z))>1&&(a=Math.sqrt(a),T*=a,z*=a);s=(new SVG.Matrix).rotate(I).scale(1/T,1/z).rotate(-I),F=F.transform(s),R=R.transform(s),r=[R.x-F.x,R.y-F.y],o=r[0]*r[0]+r[1]*r[1],n=Math.sqrt(o),r[0]/=n,r[1]/=n,l=o<4?Math.sqrt(1-o/4):0,M===E&&(l*=-1);h=new SVG.Point((R.x+F.x)/2+l*-r[1],(R.y+F.y)/2+l*r[0]),c=new SVG.Point(F.x-h.x,F.y-h.y),d=new SVG.Point(R.x-h.x,R.y-h.y),g=Math.acos(c.x/Math.sqrt(c.x*c.x+c.y*c.y)),c.y<0&&(g*=-1);u=Math.acos(d.x/Math.sqrt(d.x*d.x+d.y*d.y)),d.y<0&&(u*=-1);E&&g>u&&(u+=2*Math.PI);!E&&g<u&&(u-=2*Math.PI);for(p=Math.ceil(2*Math.abs(g-u)/Math.PI),b=[],m=g,f=(u-g)/p,x=4*Math.tan(f/4)/3,k=0;k<=p;k++)y=Math.cos(m),v=Math.sin(m),w=new SVG.Point(h.x+y,h.y+v),b[k]=[new SVG.Point(w.x+x*v,w.y-x*y),w,new SVG.Point(w.x-x*v,w.y+x*y)],m+=f;for(b[0][0]=b[0][1].clone(),b[b.length-1][2]=b[b.length-1][1].clone(),s=(new SVG.Matrix).rotate(I).scale(T,z).rotate(-I),k=0,A=b.length;k<A;k++)b[k][0]=b[k][0].transform(s),b[k][1]=b[k][1].transform(s),b[k][2]=b[k][2].transform(s);for(k=1,A=b.length;k<A;k++)w=b[k-1][2],S=w.x,C=w.y,w=b[k][0],L=w.x,P=w.y,w=b[k][1],X=w.x,Y=w.y,D.push(["C",S,C,L,P,X,Y]);return D}(this.pos,t))[0]}return t[0]="C",this.pos=[t[5],t[6]],this.reflection=[2*t[5]-t[3],2*t[6]-t[4]],e}function s(t,e){if(!1===e)return!1;for(var i=e,a=t.length;i<a;++i)if("M"==t[i][0])return i;return!1}SVG.extend(SVG.PathArray,{morph:function(e){for(var i=this.value,a=this.parse(e),r=0,n=0,o=!1,l=!1;!1!==r||!1!==n;){var h;o=s(i,!1!==r&&r+1),l=s(a,!1!==n&&n+1),!1===r&&(r=0==(h=new SVG.PathArray(c.start).bbox()).height||0==h.width?i.push(i[0])-1:i.push(["M",h.x+h.width/2,h.y+h.height/2])-1),!1===n&&(n=0==(h=new SVG.PathArray(c.dest).bbox()).height||0==h.width?a.push(a[0])-1:a.push(["M",h.x+h.width/2,h.y+h.height/2])-1);var c=t(i,r,o,a,n,l);i=i.slice(0,r).concat(c.start,!1===o?[]:i.slice(o)),a=a.slice(0,n).concat(c.dest,!1===l?[]:a.slice(l)),r=!1!==o&&r+c.start.length,n=!1!==l&&n+c.dest.length}return this.value=i,this.destination=new SVG.PathArray,this.destination.value=a,this}})}(),
|
11 |
+
/*! svg.draggable.js - v2.2.2 - 2019-01-08
|
12 |
+
* https://github.com/svgdotjs/svg.draggable.js
|
13 |
+
* Copyright (c) 2019 Wout Fierens; Licensed MIT */
|
14 |
+
function(){function t(t){t.remember("_draggable",this),this.el=t}t.prototype.init=function(t,e){var i=this;this.constraint=t,this.value=e,this.el.on("mousedown.drag",(function(t){i.start(t)})),this.el.on("touchstart.drag",(function(t){i.start(t)}))},t.prototype.transformPoint=function(t,e){var i=(t=t||window.event).changedTouches&&t.changedTouches[0]||t;return this.p.x=i.clientX-(e||0),this.p.y=i.clientY,this.p.matrixTransform(this.m)},t.prototype.getBBox=function(){var t=this.el.bbox();return this.el instanceof SVG.Nested&&(t=this.el.rbox()),(this.el instanceof SVG.G||this.el instanceof SVG.Use||this.el instanceof SVG.Nested)&&(t.x=this.el.x(),t.y=this.el.y()),t},t.prototype.start=function(t){if("click"!=t.type&&"mousedown"!=t.type&&"mousemove"!=t.type||1==(t.which||t.buttons)){var e=this;if(this.el.fire("beforedrag",{event:t,handler:this}),!this.el.event().defaultPrevented){t.preventDefault(),t.stopPropagation(),this.parent=this.parent||this.el.parent(SVG.Nested)||this.el.parent(SVG.Doc),this.p=this.parent.node.createSVGPoint(),this.m=this.el.node.getScreenCTM().inverse();var i,a=this.getBBox();if(this.el instanceof SVG.Text)switch(i=this.el.node.getComputedTextLength(),this.el.attr("text-anchor")){case"middle":i/=2;break;case"start":i=0}this.startPoints={point:this.transformPoint(t,i),box:a,transform:this.el.transform()},SVG.on(window,"mousemove.drag",(function(t){e.drag(t)})),SVG.on(window,"touchmove.drag",(function(t){e.drag(t)})),SVG.on(window,"mouseup.drag",(function(t){e.end(t)})),SVG.on(window,"touchend.drag",(function(t){e.end(t)})),this.el.fire("dragstart",{event:t,p:this.startPoints.point,m:this.m,handler:this})}}},t.prototype.drag=function(t){var e=this.getBBox(),i=this.transformPoint(t),a=this.startPoints.box.x+i.x-this.startPoints.point.x,s=this.startPoints.box.y+i.y-this.startPoints.point.y,r=this.constraint,n=i.x-this.startPoints.point.x,o=i.y-this.startPoints.point.y;if(this.el.fire("dragmove",{event:t,p:i,m:this.m,handler:this}),this.el.event().defaultPrevented)return i;if("function"==typeof r){var l=r.call(this.el,a,s,this.m);"boolean"==typeof l&&(l={x:l,y:l}),!0===l.x?this.el.x(a):!1!==l.x&&this.el.x(l.x),!0===l.y?this.el.y(s):!1!==l.y&&this.el.y(l.y)}else"object"==typeof r&&(null!=r.minX&&a<r.minX?n=(a=r.minX)-this.startPoints.box.x:null!=r.maxX&&a>r.maxX-e.width&&(n=(a=r.maxX-e.width)-this.startPoints.box.x),null!=r.minY&&s<r.minY?o=(s=r.minY)-this.startPoints.box.y:null!=r.maxY&&s>r.maxY-e.height&&(o=(s=r.maxY-e.height)-this.startPoints.box.y),null!=r.snapToGrid&&(a-=a%r.snapToGrid,s-=s%r.snapToGrid,n-=n%r.snapToGrid,o-=o%r.snapToGrid),this.el instanceof SVG.G?this.el.matrix(this.startPoints.transform).transform({x:n,y:o},!0):this.el.move(a,s));return i},t.prototype.end=function(t){var e=this.drag(t);this.el.fire("dragend",{event:t,p:e,m:this.m,handler:this}),SVG.off(window,"mousemove.drag"),SVG.off(window,"touchmove.drag"),SVG.off(window,"mouseup.drag"),SVG.off(window,"touchend.drag")},SVG.extend(SVG.Element,{draggable:function(e,i){"function"!=typeof e&&"object"!=typeof e||(i=e,e=!0);var a=this.remember("_draggable")||new t(this);return(e=void 0===e||e)?a.init(i||{},e):(this.off("mousedown.drag"),this.off("touchstart.drag")),this}})}.call(void 0),function(){function t(t){this.el=t,t.remember("_selectHandler",this),this.pointSelection={isSelected:!1},this.rectSelection={isSelected:!1},this.pointsList={lt:[0,0],rt:["width",0],rb:["width","height"],lb:[0,"height"],t:["width",0],r:["width","height"],b:["width","height"],l:[0,"height"]},this.pointCoord=function(t,e,i){var a="string"!=typeof t?t:e[t];return i?a/2:a},this.pointCoords=function(t,e){var i=this.pointsList[t];return{x:this.pointCoord(i[0],e,"t"===t||"b"===t),y:this.pointCoord(i[1],e,"r"===t||"l"===t)}}}t.prototype.init=function(t,e){var i=this.el.bbox();this.options={};var a=this.el.selectize.defaults.points;for(var s in this.el.selectize.defaults)this.options[s]=this.el.selectize.defaults[s],void 0!==e[s]&&(this.options[s]=e[s]);var r=["points","pointsExclude"];for(var s in r){var n=this.options[r[s]];"string"==typeof n?n=n.length>0?n.split(/\s*,\s*/i):[]:"boolean"==typeof n&&"points"===r[s]&&(n=n?a:[]),this.options[r[s]]=n}this.options.points=[a,this.options.points].reduce((function(t,e){return t.filter((function(t){return e.indexOf(t)>-1}))})),this.options.points=[this.options.points,this.options.pointsExclude].reduce((function(t,e){return t.filter((function(t){return e.indexOf(t)<0}))})),this.parent=this.el.parent(),this.nested=this.nested||this.parent.group(),this.nested.matrix(new SVG.Matrix(this.el).translate(i.x,i.y)),this.options.deepSelect&&-1!==["line","polyline","polygon"].indexOf(this.el.type)?this.selectPoints(t):this.selectRect(t),this.observe(),this.cleanup()},t.prototype.selectPoints=function(t){return this.pointSelection.isSelected=t,this.pointSelection.set||(this.pointSelection.set=this.parent.set(),this.drawPoints()),this},t.prototype.getPointArray=function(){var t=this.el.bbox();return this.el.array().valueOf().map((function(e){return[e[0]-t.x,e[1]-t.y]}))},t.prototype.drawPoints=function(){for(var t=this,e=this.getPointArray(),i=0,a=e.length;i<a;++i){var s=function(e){return function(i){(i=i||window.event).preventDefault?i.preventDefault():i.returnValue=!1,i.stopPropagation();var a=i.pageX||i.touches[0].pageX,s=i.pageY||i.touches[0].pageY;t.el.fire("point",{x:a,y:s,i:e,event:i})}}(i),r=this.drawPoint(e[i][0],e[i][1]).addClass(this.options.classPoints).addClass(this.options.classPoints+"_point").on("touchstart",s).on("mousedown",s);this.pointSelection.set.add(r)}},t.prototype.drawPoint=function(t,e){var i=this.options.pointType;switch(i){case"circle":return this.drawCircle(t,e);case"rect":return this.drawRect(t,e);default:if("function"==typeof i)return i.call(this,t,e);throw new Error("Unknown "+i+" point type!")}},t.prototype.drawCircle=function(t,e){return this.nested.circle(this.options.pointSize).center(t,e)},t.prototype.drawRect=function(t,e){return this.nested.rect(this.options.pointSize,this.options.pointSize).center(t,e)},t.prototype.updatePointSelection=function(){var t=this.getPointArray();this.pointSelection.set.each((function(e){this.cx()===t[e][0]&&this.cy()===t[e][1]||this.center(t[e][0],t[e][1])}))},t.prototype.updateRectSelection=function(){var t=this,e=this.el.bbox();if(this.rectSelection.set.get(0).attr({width:e.width,height:e.height}),this.options.points.length&&this.options.points.map((function(i,a){var s=t.pointCoords(i,e);t.rectSelection.set.get(a+1).center(s.x,s.y)})),this.options.rotationPoint){var i=this.rectSelection.set.length();this.rectSelection.set.get(i-1).center(e.width/2,20)}},t.prototype.selectRect=function(t){var e=this,i=this.el.bbox();function a(t){return function(i){(i=i||window.event).preventDefault?i.preventDefault():i.returnValue=!1,i.stopPropagation();var a=i.pageX||i.touches[0].pageX,s=i.pageY||i.touches[0].pageY;e.el.fire(t,{x:a,y:s,event:i})}}if(this.rectSelection.isSelected=t,this.rectSelection.set=this.rectSelection.set||this.parent.set(),this.rectSelection.set.get(0)||this.rectSelection.set.add(this.nested.rect(i.width,i.height).addClass(this.options.classRect)),this.options.points.length&&this.rectSelection.set.length()<2){this.options.points.map((function(t,s){var r=e.pointCoords(t,i),n=e.drawPoint(r.x,r.y).attr("class",e.options.classPoints+"_"+t).on("mousedown",a(t)).on("touchstart",a(t));e.rectSelection.set.add(n)})),this.rectSelection.set.each((function(){this.addClass(e.options.classPoints)}))}if(this.options.rotationPoint&&(this.options.points&&!this.rectSelection.set.get(9)||!this.options.points&&!this.rectSelection.set.get(1))){var s=function(t){(t=t||window.event).preventDefault?t.preventDefault():t.returnValue=!1,t.stopPropagation();var i=t.pageX||t.touches[0].pageX,a=t.pageY||t.touches[0].pageY;e.el.fire("rot",{x:i,y:a,event:t})},r=this.drawPoint(i.width/2,20).attr("class",this.options.classPoints+"_rot").on("touchstart",s).on("mousedown",s);this.rectSelection.set.add(r)}},t.prototype.handler=function(){var t=this.el.bbox();this.nested.matrix(new SVG.Matrix(this.el).translate(t.x,t.y)),this.rectSelection.isSelected&&this.updateRectSelection(),this.pointSelection.isSelected&&this.updatePointSelection()},t.prototype.observe=function(){var t=this;if(MutationObserver)if(this.rectSelection.isSelected||this.pointSelection.isSelected)this.observerInst=this.observerInst||new MutationObserver((function(){t.handler()})),this.observerInst.observe(this.el.node,{attributes:!0});else try{this.observerInst.disconnect(),delete this.observerInst}catch(t){}else this.el.off("DOMAttrModified.select"),(this.rectSelection.isSelected||this.pointSelection.isSelected)&&this.el.on("DOMAttrModified.select",(function(){t.handler()}))},t.prototype.cleanup=function(){!this.rectSelection.isSelected&&this.rectSelection.set&&(this.rectSelection.set.each((function(){this.remove()})),this.rectSelection.set.clear(),delete this.rectSelection.set),!this.pointSelection.isSelected&&this.pointSelection.set&&(this.pointSelection.set.each((function(){this.remove()})),this.pointSelection.set.clear(),delete this.pointSelection.set),this.pointSelection.isSelected||this.rectSelection.isSelected||(this.nested.remove(),delete this.nested)},SVG.extend(SVG.Element,{selectize:function(e,i){return"object"==typeof e&&(i=e,e=!0),(this.remember("_selectHandler")||new t(this)).init(void 0===e||e,i||{}),this}}),SVG.Element.prototype.selectize.defaults={points:["lt","rt","rb","lb","t","r","b","l"],pointsExclude:[],classRect:"svg_select_boundingRect",classPoints:"svg_select_points",pointSize:7,rotationPoint:!0,deepSelect:!1,pointType:"circle"}}(),function(){(function(){function t(t){t.remember("_resizeHandler",this),this.el=t,this.parameters={},this.lastUpdateCall=null,this.p=t.doc().node.createSVGPoint()}t.prototype.transformPoint=function(t,e,i){return this.p.x=t-(this.offset.x-window.pageXOffset),this.p.y=e-(this.offset.y-window.pageYOffset),this.p.matrixTransform(i||this.m)},t.prototype._extractPosition=function(t){return{x:null!=t.clientX?t.clientX:t.touches[0].clientX,y:null!=t.clientY?t.clientY:t.touches[0].clientY}},t.prototype.init=function(t){var e=this;if(this.stop(),"stop"!==t){for(var i in this.options={},this.el.resize.defaults)this.options[i]=this.el.resize.defaults[i],void 0!==t[i]&&(this.options[i]=t[i]);this.el.on("lt.resize",(function(t){e.resize(t||window.event)})),this.el.on("rt.resize",(function(t){e.resize(t||window.event)})),this.el.on("rb.resize",(function(t){e.resize(t||window.event)})),this.el.on("lb.resize",(function(t){e.resize(t||window.event)})),this.el.on("t.resize",(function(t){e.resize(t||window.event)})),this.el.on("r.resize",(function(t){e.resize(t||window.event)})),this.el.on("b.resize",(function(t){e.resize(t||window.event)})),this.el.on("l.resize",(function(t){e.resize(t||window.event)})),this.el.on("rot.resize",(function(t){e.resize(t||window.event)})),this.el.on("point.resize",(function(t){e.resize(t||window.event)})),this.update()}},t.prototype.stop=function(){return this.el.off("lt.resize"),this.el.off("rt.resize"),this.el.off("rb.resize"),this.el.off("lb.resize"),this.el.off("t.resize"),this.el.off("r.resize"),this.el.off("b.resize"),this.el.off("l.resize"),this.el.off("rot.resize"),this.el.off("point.resize"),this},t.prototype.resize=function(t){var e=this;this.m=this.el.node.getScreenCTM().inverse(),this.offset={x:window.pageXOffset,y:window.pageYOffset};var i=this._extractPosition(t.detail.event);if(this.parameters={type:this.el.type,p:this.transformPoint(i.x,i.y),x:t.detail.x,y:t.detail.y,box:this.el.bbox(),rotation:this.el.transform().rotation},"text"===this.el.type&&(this.parameters.fontSize=this.el.attr()["font-size"]),void 0!==t.detail.i){var a=this.el.array().valueOf();this.parameters.i=t.detail.i,this.parameters.pointCoords=[a[t.detail.i][0],a[t.detail.i][1]]}switch(t.type){case"lt":this.calc=function(t,e){var i=this.snapToGrid(t,e);if(this.parameters.box.width-i[0]>0&&this.parameters.box.height-i[1]>0){if("text"===this.parameters.type)return this.el.move(this.parameters.box.x+i[0],this.parameters.box.y),void this.el.attr("font-size",this.parameters.fontSize-i[0]);i=this.checkAspectRatio(i),this.el.move(this.parameters.box.x+i[0],this.parameters.box.y+i[1]).size(this.parameters.box.width-i[0],this.parameters.box.height-i[1])}};break;case"rt":this.calc=function(t,e){var i=this.snapToGrid(t,e,2);if(this.parameters.box.width+i[0]>0&&this.parameters.box.height-i[1]>0){if("text"===this.parameters.type)return this.el.move(this.parameters.box.x-i[0],this.parameters.box.y),void this.el.attr("font-size",this.parameters.fontSize+i[0]);i=this.checkAspectRatio(i,!0),this.el.move(this.parameters.box.x,this.parameters.box.y+i[1]).size(this.parameters.box.width+i[0],this.parameters.box.height-i[1])}};break;case"rb":this.calc=function(t,e){var i=this.snapToGrid(t,e,0);if(this.parameters.box.width+i[0]>0&&this.parameters.box.height+i[1]>0){if("text"===this.parameters.type)return this.el.move(this.parameters.box.x-i[0],this.parameters.box.y),void this.el.attr("font-size",this.parameters.fontSize+i[0]);i=this.checkAspectRatio(i),this.el.move(this.parameters.box.x,this.parameters.box.y).size(this.parameters.box.width+i[0],this.parameters.box.height+i[1])}};break;case"lb":this.calc=function(t,e){var i=this.snapToGrid(t,e,1);if(this.parameters.box.width-i[0]>0&&this.parameters.box.height+i[1]>0){if("text"===this.parameters.type)return this.el.move(this.parameters.box.x+i[0],this.parameters.box.y),void this.el.attr("font-size",this.parameters.fontSize-i[0]);i=this.checkAspectRatio(i,!0),this.el.move(this.parameters.box.x+i[0],this.parameters.box.y).size(this.parameters.box.width-i[0],this.parameters.box.height+i[1])}};break;case"t":this.calc=function(t,e){var i=this.snapToGrid(t,e,2);if(this.parameters.box.height-i[1]>0){if("text"===this.parameters.type)return;this.el.move(this.parameters.box.x,this.parameters.box.y+i[1]).height(this.parameters.box.height-i[1])}};break;case"r":this.calc=function(t,e){var i=this.snapToGrid(t,e,0);if(this.parameters.box.width+i[0]>0){if("text"===this.parameters.type)return;this.el.move(this.parameters.box.x,this.parameters.box.y).width(this.parameters.box.width+i[0])}};break;case"b":this.calc=function(t,e){var i=this.snapToGrid(t,e,0);if(this.parameters.box.height+i[1]>0){if("text"===this.parameters.type)return;this.el.move(this.parameters.box.x,this.parameters.box.y).height(this.parameters.box.height+i[1])}};break;case"l":this.calc=function(t,e){var i=this.snapToGrid(t,e,1);if(this.parameters.box.width-i[0]>0){if("text"===this.parameters.type)return;this.el.move(this.parameters.box.x+i[0],this.parameters.box.y).width(this.parameters.box.width-i[0])}};break;case"rot":this.calc=function(t,e){var i=t+this.parameters.p.x,a=e+this.parameters.p.y,s=Math.atan2(this.parameters.p.y-this.parameters.box.y-this.parameters.box.height/2,this.parameters.p.x-this.parameters.box.x-this.parameters.box.width/2),r=Math.atan2(a-this.parameters.box.y-this.parameters.box.height/2,i-this.parameters.box.x-this.parameters.box.width/2),n=this.parameters.rotation+180*(r-s)/Math.PI+this.options.snapToAngle/2;this.el.center(this.parameters.box.cx,this.parameters.box.cy).rotate(n-n%this.options.snapToAngle,this.parameters.box.cx,this.parameters.box.cy)};break;case"point":this.calc=function(t,e){var i=this.snapToGrid(t,e,this.parameters.pointCoords[0],this.parameters.pointCoords[1]),a=this.el.array().valueOf();a[this.parameters.i][0]=this.parameters.pointCoords[0]+i[0],a[this.parameters.i][1]=this.parameters.pointCoords[1]+i[1],this.el.plot(a)}}this.el.fire("resizestart",{dx:this.parameters.x,dy:this.parameters.y,event:t}),SVG.on(window,"touchmove.resize",(function(t){e.update(t||window.event)})),SVG.on(window,"touchend.resize",(function(){e.done()})),SVG.on(window,"mousemove.resize",(function(t){e.update(t||window.event)})),SVG.on(window,"mouseup.resize",(function(){e.done()}))},t.prototype.update=function(t){if(t){var e=this._extractPosition(t),i=this.transformPoint(e.x,e.y),a=i.x-this.parameters.p.x,s=i.y-this.parameters.p.y;this.lastUpdateCall=[a,s],this.calc(a,s),this.el.fire("resizing",{dx:a,dy:s,event:t})}else this.lastUpdateCall&&this.calc(this.lastUpdateCall[0],this.lastUpdateCall[1])},t.prototype.done=function(){this.lastUpdateCall=null,SVG.off(window,"mousemove.resize"),SVG.off(window,"mouseup.resize"),SVG.off(window,"touchmove.resize"),SVG.off(window,"touchend.resize"),this.el.fire("resizedone")},t.prototype.snapToGrid=function(t,e,i,a){var s;return void 0!==a?s=[(i+t)%this.options.snapToGrid,(a+e)%this.options.snapToGrid]:(i=null==i?3:i,s=[(this.parameters.box.x+t+(1&i?0:this.parameters.box.width))%this.options.snapToGrid,(this.parameters.box.y+e+(2&i?0:this.parameters.box.height))%this.options.snapToGrid]),t<0&&(s[0]-=this.options.snapToGrid),e<0&&(s[1]-=this.options.snapToGrid),t-=Math.abs(s[0])<this.options.snapToGrid/2?s[0]:s[0]-(t<0?-this.options.snapToGrid:this.options.snapToGrid),e-=Math.abs(s[1])<this.options.snapToGrid/2?s[1]:s[1]-(e<0?-this.options.snapToGrid:this.options.snapToGrid),this.constraintToBox(t,e,i,a)},t.prototype.constraintToBox=function(t,e,i,a){var s,r,n=this.options.constraint||{};return void 0!==a?(s=i,r=a):(s=this.parameters.box.x+(1&i?0:this.parameters.box.width),r=this.parameters.box.y+(2&i?0:this.parameters.box.height)),void 0!==n.minX&&s+t<n.minX&&(t=n.minX-s),void 0!==n.maxX&&s+t>n.maxX&&(t=n.maxX-s),void 0!==n.minY&&r+e<n.minY&&(e=n.minY-r),void 0!==n.maxY&&r+e>n.maxY&&(e=n.maxY-r),[t,e]},t.prototype.checkAspectRatio=function(t,e){if(!this.options.saveAspectRatio)return t;var i=t.slice(),a=this.parameters.box.width/this.parameters.box.height,s=this.parameters.box.width+t[0],r=this.parameters.box.height-t[1],n=s/r;return n<a?(i[1]=s/a-this.parameters.box.height,e&&(i[1]=-i[1])):n>a&&(i[0]=this.parameters.box.width-r*a,e&&(i[0]=-i[0])),i},SVG.extend(SVG.Element,{resize:function(e){return(this.remember("_resizeHandler")||new t(this)).init(e||{}),this}}),SVG.Element.prototype.resize.defaults={snapToAngle:.1,snapToGrid:1,constraint:{},saveAspectRatio:!1}}).call(this)}();!function(t,e){void 0===e&&(e={});var i=e.insertAt;if(t&&"undefined"!=typeof document){var a=document.head||document.getElementsByTagName("head")[0],s=document.createElement("style");s.type="text/css","top"===i&&a.firstChild?a.insertBefore(s,a.firstChild):a.appendChild(s),s.styleSheet?s.styleSheet.cssText=t:s.appendChild(document.createTextNode(t))}}('.apexcharts-canvas {\n position: relative;\n user-select: none;\n /* cannot give overflow: hidden as it will crop tooltips which overflow outside chart area */\n}\n\n\n/* scrollbar is not visible by default for legend, hence forcing the visibility */\n.apexcharts-canvas ::-webkit-scrollbar {\n -webkit-appearance: none;\n width: 6px;\n}\n\n.apexcharts-canvas ::-webkit-scrollbar-thumb {\n border-radius: 4px;\n background-color: rgba(0, 0, 0, .5);\n box-shadow: 0 0 1px rgba(255, 255, 255, .5);\n -webkit-box-shadow: 0 0 1px rgba(255, 255, 255, .5);\n}\n\n\n.apexcharts-inner {\n position: relative;\n}\n\n.apexcharts-text tspan {\n font-family: inherit;\n}\n\n.legend-mouseover-inactive {\n transition: 0.15s ease all;\n opacity: 0.20;\n}\n\n.apexcharts-series-collapsed {\n opacity: 0;\n}\n\n.apexcharts-tooltip {\n border-radius: 5px;\n box-shadow: 2px 2px 6px -4px #999;\n cursor: default;\n font-size: 14px;\n left: 62px;\n opacity: 0;\n pointer-events: none;\n position: absolute;\n top: 20px;\n overflow: hidden;\n white-space: nowrap;\n z-index: 12;\n transition: 0.15s ease all;\n}\n\n.apexcharts-tooltip.apexcharts-active {\n opacity: 1;\n transition: 0.15s ease all;\n}\n\n.apexcharts-tooltip.apexcharts-theme-light {\n border: 1px solid #e3e3e3;\n background: rgba(255, 255, 255, 0.96);\n}\n\n.apexcharts-tooltip.apexcharts-theme-dark {\n color: #fff;\n background: rgba(30, 30, 30, 0.8);\n}\n\n.apexcharts-tooltip * {\n font-family: inherit;\n}\n\n\n.apexcharts-tooltip-title {\n padding: 6px;\n font-size: 15px;\n margin-bottom: 4px;\n}\n\n.apexcharts-tooltip.apexcharts-theme-light .apexcharts-tooltip-title {\n background: #ECEFF1;\n border-bottom: 1px solid #ddd;\n}\n\n.apexcharts-tooltip.apexcharts-theme-dark .apexcharts-tooltip-title {\n background: rgba(0, 0, 0, 0.7);\n border-bottom: 1px solid #333;\n}\n\n.apexcharts-tooltip-text-value,\n.apexcharts-tooltip-text-z-value {\n display: inline-block;\n font-weight: 600;\n margin-left: 5px;\n}\n\n.apexcharts-tooltip-text-z-label:empty,\n.apexcharts-tooltip-text-z-value:empty {\n display: none;\n}\n\n.apexcharts-tooltip-text-value,\n.apexcharts-tooltip-text-z-value {\n font-weight: 600;\n}\n\n.apexcharts-tooltip-marker {\n width: 12px;\n height: 12px;\n position: relative;\n top: 0px;\n margin-right: 10px;\n border-radius: 50%;\n}\n\n.apexcharts-tooltip-series-group {\n padding: 0 10px;\n display: none;\n text-align: left;\n justify-content: left;\n align-items: center;\n}\n\n.apexcharts-tooltip-series-group.apexcharts-active .apexcharts-tooltip-marker {\n opacity: 1;\n}\n\n.apexcharts-tooltip-series-group.apexcharts-active,\n.apexcharts-tooltip-series-group:last-child {\n padding-bottom: 4px;\n}\n\n.apexcharts-tooltip-series-group-hidden {\n opacity: 0;\n height: 0;\n line-height: 0;\n padding: 0 !important;\n}\n\n.apexcharts-tooltip-y-group {\n padding: 6px 0 5px;\n}\n\n.apexcharts-tooltip-candlestick {\n padding: 4px 8px;\n}\n\n.apexcharts-tooltip-candlestick>div {\n margin: 4px 0;\n}\n\n.apexcharts-tooltip-candlestick span.value {\n font-weight: bold;\n}\n\n.apexcharts-tooltip-rangebar {\n padding: 5px 8px;\n}\n\n.apexcharts-tooltip-rangebar .category {\n font-weight: 600;\n color: #777;\n}\n\n.apexcharts-tooltip-rangebar .series-name {\n font-weight: bold;\n display: block;\n margin-bottom: 5px;\n}\n\n.apexcharts-xaxistooltip {\n opacity: 0;\n padding: 9px 10px;\n pointer-events: none;\n color: #373d3f;\n font-size: 13px;\n text-align: center;\n border-radius: 2px;\n position: absolute;\n z-index: 10;\n background: #ECEFF1;\n border: 1px solid #90A4AE;\n transition: 0.15s ease all;\n}\n\n.apexcharts-xaxistooltip.apexcharts-theme-dark {\n background: rgba(0, 0, 0, 0.7);\n border: 1px solid rgba(0, 0, 0, 0.5);\n color: #fff;\n}\n\n.apexcharts-xaxistooltip:after,\n.apexcharts-xaxistooltip:before {\n left: 50%;\n border: solid transparent;\n content: " ";\n height: 0;\n width: 0;\n position: absolute;\n pointer-events: none;\n}\n\n.apexcharts-xaxistooltip:after {\n border-color: rgba(236, 239, 241, 0);\n border-width: 6px;\n margin-left: -6px;\n}\n\n.apexcharts-xaxistooltip:before {\n border-color: rgba(144, 164, 174, 0);\n border-width: 7px;\n margin-left: -7px;\n}\n\n.apexcharts-xaxistooltip-bottom:after,\n.apexcharts-xaxistooltip-bottom:before {\n bottom: 100%;\n}\n\n.apexcharts-xaxistooltip-top:after,\n.apexcharts-xaxistooltip-top:before {\n top: 100%;\n}\n\n.apexcharts-xaxistooltip-bottom:after {\n border-bottom-color: #ECEFF1;\n}\n\n.apexcharts-xaxistooltip-bottom:before {\n border-bottom-color: #90A4AE;\n}\n\n.apexcharts-xaxistooltip-bottom.apexcharts-theme-dark:after {\n border-bottom-color: rgba(0, 0, 0, 0.5);\n}\n\n.apexcharts-xaxistooltip-bottom.apexcharts-theme-dark:before {\n border-bottom-color: rgba(0, 0, 0, 0.5);\n}\n\n.apexcharts-xaxistooltip-top:after {\n border-top-color: #ECEFF1\n}\n\n.apexcharts-xaxistooltip-top:before {\n border-top-color: #90A4AE;\n}\n\n.apexcharts-xaxistooltip-top.apexcharts-theme-dark:after {\n border-top-color: rgba(0, 0, 0, 0.5);\n}\n\n.apexcharts-xaxistooltip-top.apexcharts-theme-dark:before {\n border-top-color: rgba(0, 0, 0, 0.5);\n}\n\n.apexcharts-xaxistooltip.apexcharts-active {\n opacity: 1;\n transition: 0.15s ease all;\n}\n\n.apexcharts-yaxistooltip {\n opacity: 0;\n padding: 4px 10px;\n pointer-events: none;\n color: #373d3f;\n font-size: 13px;\n text-align: center;\n border-radius: 2px;\n position: absolute;\n z-index: 10;\n background: #ECEFF1;\n border: 1px solid #90A4AE;\n}\n\n.apexcharts-yaxistooltip.apexcharts-theme-dark {\n background: rgba(0, 0, 0, 0.7);\n border: 1px solid rgba(0, 0, 0, 0.5);\n color: #fff;\n}\n\n.apexcharts-yaxistooltip:after,\n.apexcharts-yaxistooltip:before {\n top: 50%;\n border: solid transparent;\n content: " ";\n height: 0;\n width: 0;\n position: absolute;\n pointer-events: none;\n}\n\n.apexcharts-yaxistooltip:after {\n border-color: rgba(236, 239, 241, 0);\n border-width: 6px;\n margin-top: -6px;\n}\n\n.apexcharts-yaxistooltip:before {\n border-color: rgba(144, 164, 174, 0);\n border-width: 7px;\n margin-top: -7px;\n}\n\n.apexcharts-yaxistooltip-left:after,\n.apexcharts-yaxistooltip-left:before {\n left: 100%;\n}\n\n.apexcharts-yaxistooltip-right:after,\n.apexcharts-yaxistooltip-right:before {\n right: 100%;\n}\n\n.apexcharts-yaxistooltip-left:after {\n border-left-color: #ECEFF1;\n}\n\n.apexcharts-yaxistooltip-left:before {\n border-left-color: #90A4AE;\n}\n\n.apexcharts-yaxistooltip-left.apexcharts-theme-dark:after {\n border-left-color: rgba(0, 0, 0, 0.5);\n}\n\n.apexcharts-yaxistooltip-left.apexcharts-theme-dark:before {\n border-left-color: rgba(0, 0, 0, 0.5);\n}\n\n.apexcharts-yaxistooltip-right:after {\n border-right-color: #ECEFF1;\n}\n\n.apexcharts-yaxistooltip-right:before {\n border-right-color: #90A4AE;\n}\n\n.apexcharts-yaxistooltip-right.apexcharts-theme-dark:after {\n border-right-color: rgba(0, 0, 0, 0.5);\n}\n\n.apexcharts-yaxistooltip-right.apexcharts-theme-dark:before {\n border-right-color: rgba(0, 0, 0, 0.5);\n}\n\n.apexcharts-yaxistooltip.apexcharts-active {\n opacity: 1;\n}\n\n.apexcharts-yaxistooltip-hidden {\n display: none;\n}\n\n.apexcharts-xcrosshairs,\n.apexcharts-ycrosshairs {\n pointer-events: none;\n opacity: 0;\n transition: 0.15s ease all;\n}\n\n.apexcharts-xcrosshairs.apexcharts-active,\n.apexcharts-ycrosshairs.apexcharts-active {\n opacity: 1;\n transition: 0.15s ease all;\n}\n\n.apexcharts-ycrosshairs-hidden {\n opacity: 0;\n}\n\n.apexcharts-selection-rect {\n cursor: move;\n}\n\n.svg_select_boundingRect, .svg_select_points_rot {\n pointer-events: none;\n opacity: 0;\n visibility: hidden;\n}\n.apexcharts-selection-rect + g .svg_select_boundingRect,\n.apexcharts-selection-rect + g .svg_select_points_rot {\n opacity: 0;\n visibility: hidden;\n}\n\n.apexcharts-selection-rect + g .svg_select_points_l,\n.apexcharts-selection-rect + g .svg_select_points_r {\n cursor: ew-resize;\n opacity: 1;\n visibility: visible;\n}\n\n.svg_select_points {\n fill: #efefef;\n stroke: #333;\n rx: 2;\n}\n\n.apexcharts-canvas.apexcharts-zoomable .hovering-zoom {\n cursor: crosshair\n}\n\n.apexcharts-canvas.apexcharts-zoomable .hovering-pan {\n cursor: move\n}\n\n.apexcharts-zoom-icon,\n.apexcharts-zoomin-icon,\n.apexcharts-zoomout-icon,\n.apexcharts-reset-icon,\n.apexcharts-pan-icon,\n.apexcharts-selection-icon,\n.apexcharts-menu-icon,\n.apexcharts-toolbar-custom-icon {\n cursor: pointer;\n width: 20px;\n height: 20px;\n line-height: 24px;\n color: #6E8192;\n text-align: center;\n}\n\n.apexcharts-zoom-icon svg,\n.apexcharts-zoomin-icon svg,\n.apexcharts-zoomout-icon svg,\n.apexcharts-reset-icon svg,\n.apexcharts-menu-icon svg {\n fill: #6E8192;\n}\n\n.apexcharts-selection-icon svg {\n fill: #444;\n transform: scale(0.76)\n}\n\n.apexcharts-theme-dark .apexcharts-zoom-icon svg,\n.apexcharts-theme-dark .apexcharts-zoomin-icon svg,\n.apexcharts-theme-dark .apexcharts-zoomout-icon svg,\n.apexcharts-theme-dark .apexcharts-reset-icon svg,\n.apexcharts-theme-dark .apexcharts-pan-icon svg,\n.apexcharts-theme-dark .apexcharts-selection-icon svg,\n.apexcharts-theme-dark .apexcharts-menu-icon svg,\n.apexcharts-theme-dark .apexcharts-toolbar-custom-icon svg {\n fill: #f3f4f5;\n}\n\n.apexcharts-canvas .apexcharts-zoom-icon.apexcharts-selected svg,\n.apexcharts-canvas .apexcharts-selection-icon.apexcharts-selected svg,\n.apexcharts-canvas .apexcharts-reset-zoom-icon.apexcharts-selected svg {\n fill: #008FFB;\n}\n\n.apexcharts-theme-light .apexcharts-selection-icon:not(.apexcharts-selected):hover svg,\n.apexcharts-theme-light .apexcharts-zoom-icon:not(.apexcharts-selected):hover svg,\n.apexcharts-theme-light .apexcharts-zoomin-icon:hover svg,\n.apexcharts-theme-light .apexcharts-zoomout-icon:hover svg,\n.apexcharts-theme-light .apexcharts-reset-icon:hover svg,\n.apexcharts-theme-light .apexcharts-menu-icon:hover svg {\n fill: #333;\n}\n\n.apexcharts-selection-icon,\n.apexcharts-menu-icon {\n position: relative;\n}\n\n.apexcharts-reset-icon {\n margin-left: 5px;\n}\n\n.apexcharts-zoom-icon,\n.apexcharts-reset-icon,\n.apexcharts-menu-icon {\n transform: scale(0.85);\n}\n\n.apexcharts-zoomin-icon,\n.apexcharts-zoomout-icon {\n transform: scale(0.7)\n}\n\n.apexcharts-zoomout-icon {\n margin-right: 3px;\n}\n\n.apexcharts-pan-icon {\n transform: scale(0.62);\n position: relative;\n left: 1px;\n top: 0px;\n}\n\n.apexcharts-pan-icon svg {\n fill: #fff;\n stroke: #6E8192;\n stroke-width: 2;\n}\n\n.apexcharts-pan-icon.apexcharts-selected svg {\n stroke: #008FFB;\n}\n\n.apexcharts-pan-icon:not(.apexcharts-selected):hover svg {\n stroke: #333;\n}\n\n.apexcharts-toolbar {\n position: absolute;\n z-index: 11;\n max-width: 176px;\n text-align: right;\n border-radius: 3px;\n padding: 0px 6px 2px 6px;\n display: flex;\n justify-content: space-between;\n align-items: center;\n}\n\n.apexcharts-menu {\n background: #fff;\n position: absolute;\n top: 100%;\n border: 1px solid #ddd;\n border-radius: 3px;\n padding: 3px;\n right: 10px;\n opacity: 0;\n min-width: 110px;\n transition: 0.15s ease all;\n pointer-events: none;\n}\n\n.apexcharts-menu.apexcharts-menu-open {\n opacity: 1;\n pointer-events: all;\n transition: 0.15s ease all;\n}\n\n.apexcharts-menu-item {\n padding: 6px 7px;\n font-size: 12px;\n cursor: pointer;\n}\n\n.apexcharts-theme-light .apexcharts-menu-item:hover {\n background: #eee;\n}\n\n.apexcharts-theme-dark .apexcharts-menu {\n background: rgba(0, 0, 0, 0.7);\n color: #fff;\n}\n\n@media screen and (min-width: 768px) {\n .apexcharts-canvas:hover .apexcharts-toolbar {\n opacity: 1;\n }\n}\n\n.apexcharts-datalabel.apexcharts-element-hidden {\n opacity: 0;\n}\n\n.apexcharts-pie-label,\n.apexcharts-datalabels,\n.apexcharts-datalabel,\n.apexcharts-datalabel-label,\n.apexcharts-datalabel-value {\n cursor: default;\n pointer-events: none;\n}\n\n.apexcharts-pie-label-delay {\n opacity: 0;\n animation-name: opaque;\n animation-duration: 0.3s;\n animation-fill-mode: forwards;\n animation-timing-function: ease;\n}\n\n.apexcharts-canvas .apexcharts-element-hidden {\n opacity: 0;\n}\n\n.apexcharts-hide .apexcharts-series-points {\n opacity: 0;\n}\n\n.apexcharts-gridline,\n.apexcharts-annotation-rect,\n.apexcharts-tooltip .apexcharts-marker,\n.apexcharts-area-series .apexcharts-area,\n.apexcharts-line,\n.apexcharts-zoom-rect,\n.apexcharts-toolbar svg,\n.apexcharts-area-series .apexcharts-series-markers .apexcharts-marker.no-pointer-events,\n.apexcharts-line-series .apexcharts-series-markers .apexcharts-marker.no-pointer-events,\n.apexcharts-radar-series path,\n.apexcharts-radar-series polygon {\n pointer-events: none;\n}\n\n\n/* markers */\n\n.apexcharts-marker {\n transition: 0.15s ease all;\n}\n\n@keyframes opaque {\n 0% {\n opacity: 0;\n }\n 100% {\n opacity: 1;\n }\n}\n\n\n/* Resize generated styles */\n\n@keyframes resizeanim {\n from {\n opacity: 0;\n }\n to {\n opacity: 0;\n }\n}\n\n.resize-triggers {\n animation: 1ms resizeanim;\n visibility: hidden;\n opacity: 0;\n}\n\n.resize-triggers,\n.resize-triggers>div,\n.contract-trigger:before {\n content: " ";\n display: block;\n position: absolute;\n top: 0;\n left: 0;\n height: 100%;\n width: 100%;\n overflow: hidden;\n}\n\n.resize-triggers>div {\n background: #eee;\n overflow: auto;\n}\n\n.contract-trigger:before {\n width: 200%;\n height: 200%;\n}'),function(){function t(t){var e=t.__resizeTriggers__,i=e.firstElementChild,a=e.lastElementChild,s=i?i.firstElementChild:null;a&&(a.scrollLeft=a.scrollWidth,a.scrollTop=a.scrollHeight),s&&(s.style.width=i.offsetWidth+1+"px",s.style.height=i.offsetHeight+1+"px"),i&&(i.scrollLeft=i.scrollWidth,i.scrollTop=i.scrollHeight)}function e(e){var i=this;t(this),this.__resizeRAF__&&r(this.__resizeRAF__),this.__resizeRAF__=s((function(){(function(t){return t.offsetWidth!=t.__resizeLast__.width||t.offsetHeight!=t.__resizeLast__.height})(i)&&(i.__resizeLast__.width=i.offsetWidth,i.__resizeLast__.height=i.offsetHeight,i.__resizeListeners__.forEach((function(t){t.call(e)})))}))}var i,a,s=(i=window.requestAnimationFrame||window.mozRequestAnimationFrame||window.webkitRequestAnimationFrame||function(t){return window.setTimeout(t,20)},function(t){return i(t)}),r=(a=window.cancelAnimationFrame||window.mozCancelAnimationFrame||window.webkitCancelAnimationFrame||window.clearTimeout,function(t){return a(t)}),n=!1,o="animationstart",l="Webkit Moz O ms".split(" "),h="webkitAnimationStart animationstart oAnimationStart MSAnimationStart".split(" "),c=document.createElement("fakeelement");if(void 0!==c.style.animationName&&(n=!0),!1===n)for(var d=0;d<l.length;d++)if(void 0!==c.style[l[d]+"AnimationName"]){o=h[d];break}window.addResizeListener=function(i,a){i.__resizeTriggers__||("static"==getComputedStyle(i).position&&(i.style.position="relative"),i.__resizeLast__={},i.__resizeListeners__=[],(i.__resizeTriggers__=document.createElement("div")).className="resize-triggers",i.__resizeTriggers__.innerHTML='<div class="expand-trigger"><div></div></div><div class="contract-trigger"></div>',i.appendChild(i.__resizeTriggers__),t(i),i.addEventListener("scroll",e,!0),o&&i.__resizeTriggers__.addEventListener(o,(function(e){"resizeanim"==e.animationName&&t(i)}))),i.__resizeListeners__.push(a)},window.removeResizeListener=function(t,i){t&&(t.__resizeListeners__.splice(t.__resizeListeners__.indexOf(i),1),t.__resizeListeners__.length||(t.removeEventListener("scroll",e),t.__resizeTriggers__.parentNode&&(t.__resizeTriggers__=!t.removeChild(t.__resizeTriggers__))))}}(),window.Apex={};var Et=function(){function t(i){e(this,t),this.ctx=i,this.w=i.w}return a(t,[{key:"initModules",value:function(){this.ctx.publicMethods=["updateOptions","updateSeries","appendData","appendSeries","toggleSeries","showSeries","hideSeries","setLocale","resetSeries","zoomX","toggleDataPointSelection","dataURI","addXaxisAnnotation","addYaxisAnnotation","addPointAnnotation","clearAnnotations","removeAnnotation","paper","destroy"],this.ctx.eventList=["click","mousedown","mousemove","touchstart","touchmove","mouseup","touchend"],this.ctx.animations=new x(this.ctx),this.ctx.axes=new K(this.ctx),this.ctx.core=new It(this.ctx.el,this.ctx),this.ctx.config=new N({}),this.ctx.data=new B(this.ctx),this.ctx.grid=new U(this.ctx),this.ctx.graphics=new b(this.ctx),this.ctx.coreUtils=new y(this.ctx),this.ctx.crosshairs=new tt(this.ctx),this.ctx.events=new J(this.ctx),this.ctx.exports=new _(this.ctx),this.ctx.localization=new Q(this.ctx),this.ctx.options=new L,this.ctx.responsive=new et(this.ctx),this.ctx.series=new X(this.ctx),this.ctx.theme=new it(this.ctx),this.ctx.formatters=new V(this.ctx),this.ctx.titleSubtitle=new at(this.ctx),this.ctx.legend=new ct(this.ctx),this.ctx.toolbar=new dt(this.ctx),this.ctx.dimensions=new lt(this.ctx),this.ctx.updateHelpers=new Mt(this.ctx),this.ctx.zoomPanSelection=new gt(this.ctx),this.ctx.w.globals.tooltip=new vt(this.ctx)}}]),t}(),Xt=function(){function t(i){e(this,t),this.ctx=i,this.w=i.w}return a(t,[{key:"clear",value:function(){this.ctx.zoomPanSelection&&this.ctx.zoomPanSelection.destroy(),this.ctx.toolbar&&this.ctx.toolbar.destroy(),this.ctx.animations=null,this.ctx.axes=null,this.ctx.annotations=null,this.ctx.core=null,this.ctx.data=null,this.ctx.grid=null,this.ctx.series=null,this.ctx.responsive=null,this.ctx.theme=null,this.ctx.formatters=null,this.ctx.titleSubtitle=null,this.ctx.legend=null,this.ctx.dimensions=null,this.ctx.options=null,this.ctx.crosshairs=null,this.ctx.zoomPanSelection=null,this.ctx.updateHelpers=null,this.ctx.toolbar=null,this.ctx.localization=null,this.ctx.w.globals.tooltip=null,this.clearDomElements()}},{key:"killSVG",value:function(t){t.each((function(t,e){this.removeClass("*"),this.off(),this.stop()}),!0),t.ungroup(),t.clear()}},{key:"clearDomElements",value:function(){var t=this;this.w.globals.dom.Paper.node.parentNode.parentNode.style.minHeight="unset",this.ctx.eventList.forEach((function(e){document.removeEventListener(e,t.ctx.events.documentEvent)}));var e=this.w.globals.dom;if(null!==this.ctx.el)for(;this.ctx.el.firstChild;)this.ctx.el.removeChild(this.ctx.el.firstChild);this.killSVG(e.Paper),e.Paper.remove(),e.elWrap=null,e.elGraphical=null,e.elAnnotations=null,e.elLegendWrap=null,e.baseEl=null,e.elGridRect=null,e.elGridRectMask=null,e.elGridRectMarkerMask=null,e.elDefs=null}}]),t}();return function(){function t(i,a){e(this,t),this.opts=a,this.ctx=this,this.w=new W(a).init(),this.el=i,this.w.globals.cuid=f.randomId(),this.w.globals.chartID=this.w.config.chart.id?this.w.config.chart.id:this.w.globals.cuid,new Et(this).initModules(),this.create=f.bind(this.create,this),this.windowResizeHandler=this._windowResize.bind(this)}return a(t,[{key:"render",value:function(){var t=this;return new Promise((function(e,i){if(null!==t.el){void 0===Apex._chartInstances&&(Apex._chartInstances=[]),t.w.config.chart.id&&Apex._chartInstances.push({id:t.w.globals.chartID,group:t.w.config.chart.group,chart:t}),t.setLocale(t.w.config.chart.defaultLocale);var a=t.w.config.chart.events.beforeMount;"function"==typeof a&&a(t,t.w),t.events.fireEvent("beforeMount",[t,t.w]),window.addEventListener("resize",t.windowResizeHandler),window.addResizeListener(t.el.parentNode,t._parentResizeCallback.bind(t));var s=t.create(t.w.config.series,{});if(!s)return e(t);t.mount(s).then((function(){"function"==typeof t.w.config.chart.events.mounted&&t.w.config.chart.events.mounted(t,t.w),t.events.fireEvent("mounted",[t,t.w]),e(s)})).catch((function(t){i(t)}))}else i(new Error("Element not found"))}))}},{key:"create",value:function(t,e){var i=this.w;new Et(this).initModules();var a=this.w.globals;(a.noData=!1,a.animationEnded=!1,this.responsive.checkResponsiveConfig(e),i.config.xaxis.convertedCatToNumeric)&&new H(i.config).convertCatToNumericXaxis(i.config,this.ctx);if(null===this.el)return a.animationEnded=!0,null;if(this.core.setupElements(),0===a.svgWidth)return a.animationEnded=!0,null;var s=y.checkComboSeries(t);a.comboCharts=s.comboCharts,a.comboBarCount=s.comboBarCount;var r=t.every((function(t){return t.data&&0===t.data.length}));(0===t.length||r)&&this.series.handleNoData(),this.events.setupEventHandlers(),this.data.parseData(t),this.theme.init(),new z(this).setGlobalMarkerSize(),this.formatters.setLabelFormatters(),this.titleSubtitle.draw(),a.noData&&a.collapsedSeries.length!==a.series.length&&!i.config.legend.showForSingleSeries||this.legend.init(),this.series.hasAllSeriesEqualX(),a.axisCharts&&(this.core.coreCalculations(),"category"!==i.config.xaxis.type&&this.formatters.setLabelFormatters()),this.formatters.heatmapLabelFormatters(),this.dimensions.plotCoords();var n=this.core.xySettings();this.grid.createGridMask();var o=this.core.plotChartType(t,n),l=new M(this);l.bringForward(),i.config.dataLabels.background.enabled&&l.dataLabelsBackground(),this.core.shiftGraphPosition();var h={plot:{left:i.globals.translateX,top:i.globals.translateY,width:i.globals.gridWidth,height:i.globals.gridHeight}};return{elGraph:o,xyRatios:n,elInner:i.globals.dom.elGraphical,dimensions:h}}},{key:"mount",value:function(){var t=this,e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null,i=this,a=i.w;return new Promise((function(s,r){if(null===i.el)return r(new Error("Not enough data to display or target element not found"));(null===e||a.globals.allSeriesCollapsed)&&i.series.handleNoData(),i.axes.drawAxis(a.config.chart.type,e.xyRatios),i.grid=new U(i);var n=i.grid.drawGrid();i.annotations=new P(i),i.annotations.drawShapeAnnos(),i.annotations.drawImageAnnos(),i.annotations.drawTextAnnos(),"back"===a.config.grid.position&&n&&a.globals.dom.elGraphical.add(n.el);var o=new j(t.ctx),l=new $(t.ctx);if(null!==n&&(o.xAxisLabelCorrections(n.xAxisTickWidth),l.setYAxisTextAlignments()),"back"===a.config.annotations.position&&(a.globals.dom.Paper.add(a.globals.dom.elAnnotations),i.annotations.drawAxesAnnotations()),e.elGraph instanceof Array)for(var h=0;h<e.elGraph.length;h++)a.globals.dom.elGraphical.add(e.elGraph[h]);else a.globals.dom.elGraphical.add(e.elGraph);if("front"===a.config.grid.position&&n&&a.globals.dom.elGraphical.add(n.el),"front"===a.config.xaxis.crosshairs.position&&i.crosshairs.drawXCrosshairs(),"front"===a.config.yaxis[0].crosshairs.position&&i.crosshairs.drawYCrosshairs(),"front"===a.config.annotations.position&&(a.globals.dom.Paper.add(a.globals.dom.elAnnotations),i.annotations.drawAxesAnnotations()),!a.globals.noData){if(a.config.tooltip.enabled&&!a.globals.noData&&i.w.globals.tooltip.drawTooltip(e.xyRatios),a.globals.axisCharts&&(a.globals.isXNumeric||a.config.xaxis.convertedCatToNumeric))(a.config.chart.zoom.enabled||a.config.chart.selection&&a.config.chart.selection.enabled||a.config.chart.pan&&a.config.chart.pan.enabled)&&i.zoomPanSelection.init({xyRatios:e.xyRatios});else{var c=a.config.chart.toolbar.tools;["zoom","zoomin","zoomout","selection","pan","reset"].forEach((function(t){c[t]=!1}))}a.config.chart.toolbar.show&&!a.globals.allSeriesCollapsed&&i.toolbar.createToolbar()}a.globals.memory.methodsToExec.length>0&&a.globals.memory.methodsToExec.forEach((function(t){t.method(t.params,!1,t.context)})),a.globals.axisCharts||a.globals.noData||i.core.resizeNonAxisCharts(),s(i)}))}},{key:"destroy",value:function(){window.removeEventListener("resize",this.windowResizeHandler),window.removeResizeListener(this.el.parentNode,this._parentResizeCallback.bind(this));var t=this.w.config.chart.id;t&&Apex._chartInstances.forEach((function(e,i){e.id===t&&Apex._chartInstances.splice(i,1)})),new Xt(this.ctx).clear()}},{key:"updateOptions",value:function(t){var e=this,i=arguments.length>1&&void 0!==arguments[1]&&arguments[1],a=!(arguments.length>2&&void 0!==arguments[2])||arguments[2],s=!(arguments.length>3&&void 0!==arguments[3])||arguments[3],r=!(arguments.length>4&&void 0!==arguments[4])||arguments[4],n=this.w;return n.globals.selection=void 0,t.series&&(this.series.resetSeries(!1,!0,!1),t.series.length&&t.series[0].data&&(t.series=t.series.map((function(t,i){return e.updateHelpers._extendSeries(t,i)}))),this.updateHelpers.revertDefaultAxisMinMax()),t.xaxis&&(t=this.updateHelpers.forceXAxisUpdate(t)),t.yaxis&&(t=this.updateHelpers.forceYAxisUpdate(t)),n.globals.collapsedSeriesIndices.length>0&&this.series.clearPreviousPaths(),t.theme&&(t=this.theme.updateThemeOptions(t)),this.updateHelpers._updateOptions(t,i,a,s,r)}},{key:"updateSeries",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],e=!(arguments.length>1&&void 0!==arguments[1])||arguments[1],i=!(arguments.length>2&&void 0!==arguments[2])||arguments[2];return this.series.resetSeries(!1),this.updateHelpers.revertDefaultAxisMinMax(),this.updateHelpers._updateSeries(t,e,i)}},{key:"appendSeries",value:function(t){var e=!(arguments.length>1&&void 0!==arguments[1])||arguments[1],i=!(arguments.length>2&&void 0!==arguments[2])||arguments[2],a=this.w.config.series.slice();return a.push(t),this.series.resetSeries(!1),this.updateHelpers.revertDefaultAxisMinMax(),this.updateHelpers._updateSeries(a,e,i)}},{key:"appendData",value:function(t){var e=!(arguments.length>1&&void 0!==arguments[1])||arguments[1],i=this;i.w.globals.dataChanged=!0,i.series.getPreviousPaths();for(var a=i.w.config.series.slice(),s=0;s<a.length;s++)if(null!==t[s]&&void 0!==t[s])for(var r=0;r<t[s].data.length;r++)a[s].data.push(t[s].data[r]);return i.w.config.series=a,e&&(i.w.globals.initialSeries=f.clone(i.w.config.series)),this.update()}},{key:"update",value:function(t){var e=this;return new Promise((function(i,a){new Xt(e.ctx).clear();var s=e.create(e.w.config.series,t);if(!s)return i(e);e.mount(s).then((function(){"function"==typeof e.w.config.chart.events.updated&&e.w.config.chart.events.updated(e,e.w),e.events.fireEvent("updated",[e,e.w]),e.w.globals.isDirty=!0,i(e)})).catch((function(t){a(t)}))}))}},{key:"getSyncedCharts",value:function(){var t=this.getGroupedCharts(),e=[this];return t.length&&(e=[],t.forEach((function(t){e.push(t)}))),e}},{key:"getGroupedCharts",value:function(){var t=this;return Apex._chartInstances.filter((function(t){if(t.group)return!0})).map((function(e){return t.w.config.chart.group===e.group?e.chart:t}))}},{key:"toggleSeries",value:function(t){return this.series.toggleSeries(t)}},{key:"showSeries",value:function(t){this.series.showSeries(t)}},{key:"hideSeries",value:function(t){this.series.hideSeries(t)}},{key:"resetSeries",value:function(){var t=!(arguments.length>0&&void 0!==arguments[0])||arguments[0],e=!(arguments.length>1&&void 0!==arguments[1])||arguments[1];this.series.resetSeries(t,e)}},{key:"addEventListener",value:function(t,e){this.events.addEventListener(t,e)}},{key:"removeEventListener",value:function(t,e){this.events.removeEventListener(t,e)}},{key:"addXaxisAnnotation",value:function(t){var e=!(arguments.length>1&&void 0!==arguments[1])||arguments[1],i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:void 0,a=this;i&&(a=i),a.annotations.addXaxisAnnotationExternal(t,e,a)}},{key:"addYaxisAnnotation",value:function(t){var e=!(arguments.length>1&&void 0!==arguments[1])||arguments[1],i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:void 0,a=this;i&&(a=i),a.annotations.addYaxisAnnotationExternal(t,e,a)}},{key:"addPointAnnotation",value:function(t){var e=!(arguments.length>1&&void 0!==arguments[1])||arguments[1],i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:void 0,a=this;i&&(a=i),a.annotations.addPointAnnotationExternal(t,e,a)}},{key:"clearAnnotations",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:void 0,e=this;t&&(e=t),e.annotations.clearAnnotations(e)}},{key:"removeAnnotation",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:void 0,i=this;e&&(i=e),i.annotations.removeAnnotation(i,t)}},{key:"getChartArea",value:function(){return this.w.globals.dom.baseEl.querySelector(".apexcharts-inner")}},{key:"getSeriesTotalXRange",value:function(t,e){return this.coreUtils.getSeriesTotalsXRange(t,e)}},{key:"getHighestValueInSeries",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0,e=new Z(this.ctx);return e.getMinYMaxY(t).highestY}},{key:"getLowestValueInSeries",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0,e=new Z(this.ctx);return e.getMinYMaxY(t).lowestY}},{key:"getSeriesTotal",value:function(){return this.w.globals.seriesTotals}},{key:"toggleDataPointSelection",value:function(t,e){return this.updateHelpers.toggleDataPointSelection(t,e)}},{key:"zoomX",value:function(t,e){this.ctx.toolbar.zoomUpdateOptions(t,e)}},{key:"setLocale",value:function(t){this.localization.setCurrentLocaleValues(t)}},{key:"dataURI",value:function(){return new _(this.ctx).dataURI()}},{key:"paper",value:function(){return this.w.globals.dom.Paper}},{key:"_parentResizeCallback",value:function(){!this.w.globals.noData&&this.w.globals.animationEnded&&this.w.config.chart.redrawOnParentResize&&this._windowResize()}},{key:"_windowResize",value:function(){var t=this;clearTimeout(this.w.globals.resizeTimer),this.w.globals.resizeTimer=window.setTimeout((function(){t.w.globals.resized=!0,t.w.globals.dataChanged=!1,t.ctx.update()}),150)}}],[{key:"getChartByID",value:function(t){var e=Apex._chartInstances.filter((function(e){return e.id===t}))[0];return e&&e.chart}},{key:"initOnLoad",value:function(){for(var e=document.querySelectorAll("[data-apexcharts]"),i=0;i<e.length;i++){new t(e[i],JSON.parse(e[i].getAttribute("data-options"))).render()}}},{key:"exec",value:function(t,e){var i=this.getChartByID(t);if(i){i.w.globals.isExecCalled=!0;var a=null;if(-1!==i.publicMethods.indexOf(e)){for(var s=arguments.length,r=new Array(s>2?s-2:0),n=2;n<s;n++)r[n-2]=arguments[n];a=i[e].apply(i,r)}return a}}},{key:"merge",value:function(t,e){return f.extend(t,e)}}]),t}()}));
|
dependencies/vendor/autoload.php
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
// autoload.php @generated by Composer
|
4 |
+
|
5 |
+
require_once __DIR__ . '/composer/autoload_real.php';
|
6 |
+
|
7 |
+
return ComposerAutoloaderInitf354a0cd352f791551260eb9a7611c7a::getLoader();
|
dependencies/vendor/colinmollenhour/credis/Client.php
ADDED
@@ -0,0 +1,1490 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Credis_Client (a fork of Redisent)
|
4 |
+
*
|
5 |
+
* Most commands are compatible with phpredis library:
|
6 |
+
* - use "pipeline()" to start a pipeline of commands instead of multi(Redis::PIPELINE)
|
7 |
+
* - any arrays passed as arguments will be flattened automatically
|
8 |
+
* - setOption and getOption are not supported in standalone mode
|
9 |
+
* - order of arguments follows redis-cli instead of phpredis where they differ (lrem)
|
10 |
+
*
|
11 |
+
* - Uses phpredis library if extension is installed for better performance.
|
12 |
+
* - Establishes connection lazily.
|
13 |
+
* - Supports tcp and unix sockets.
|
14 |
+
* - Reconnects automatically unless a watch or transaction is in progress.
|
15 |
+
* - Can set automatic retry connection attempts for iffy Redis connections.
|
16 |
+
*
|
17 |
+
* @author Colin Mollenhour <colin@mollenhour.com>
|
18 |
+
* @copyright 2011 Colin Mollenhour <colin@mollenhour.com>
|
19 |
+
* @license http://www.opensource.org/licenses/mit-license.php The MIT License
|
20 |
+
* @package Credis_Client
|
21 |
+
*/
|
22 |
+
|
23 |
+
if( ! defined('CRLF')) define('CRLF', sprintf('%s%s', chr(13), chr(10)));
|
24 |
+
|
25 |
+
/**
|
26 |
+
* Credis-specific errors, wraps native Redis errors
|
27 |
+
*/
|
28 |
+
class CredisException extends Exception
|
29 |
+
{
|
30 |
+
|
31 |
+
const CODE_TIMED_OUT = 1;
|
32 |
+
const CODE_DISCONNECTED = 2;
|
33 |
+
|
34 |
+
public function __construct($message, $code = 0, $exception = NULL)
|
35 |
+
{
|
36 |
+
if ($exception && get_class($exception) == 'RedisException' && strpos($message,'read error on connection') === 0) {
|
37 |
+
$code = CredisException::CODE_DISCONNECTED;
|
38 |
+
}
|
39 |
+
parent::__construct($message, $code, $exception);
|
40 |
+
}
|
41 |
+
|
42 |
+
}
|
43 |
+
|
44 |
+
/**
|
45 |
+
* Credis_Client, a lightweight Redis PHP standalone client and phpredis wrapper
|
46 |
+
*
|
47 |
+
* Server/Connection:
|
48 |
+
* @method Credis_Client pipeline()
|
49 |
+
* @method Credis_Client multi()
|
50 |
+
* @method Credis_Client watch(string ...$keys)
|
51 |
+
* @method Credis_Client unwatch()
|
52 |
+
* @method array exec()
|
53 |
+
* @method string|Credis_Client flushAll()
|
54 |
+
* @method string|Credis_Client flushDb()
|
55 |
+
* @method array|Credis_Client info(string $section = null)
|
56 |
+
* @method bool|array|Credis_Client config(string $setGet, string $key, string $value = null)
|
57 |
+
* @method array|Credis_Client role()
|
58 |
+
* @method array|Credis_Client time()
|
59 |
+
*
|
60 |
+
* Keys:
|
61 |
+
* @method int|Credis_Client del(string $key)
|
62 |
+
* @method int|Credis_Client exists(string $key)
|
63 |
+
* @method int|Credis_Client expire(string $key, int $seconds)
|
64 |
+
* @method int|Credis_Client expireAt(string $key, int $timestamp)
|
65 |
+
* @method array|Credis_Client keys(string $key)
|
66 |
+
* @method int|Credis_Client persist(string $key)
|
67 |
+
* @method bool|Credis_Client rename(string $key, string $newKey)
|
68 |
+
* @method bool|Credis_Client renameNx(string $key, string $newKey)
|
69 |
+
* @method array|Credis_Client sort(string $key, string $arg1, string $valueN = null)
|
70 |
+
* @method int|Credis_Client ttl(string $key)
|
71 |
+
* @method string|Credis_Client type(string $key)
|
72 |
+
*
|
73 |
+
* Scalars:
|
74 |
+
* @method int|Credis_Client append(string $key, string $value)
|
75 |
+
* @method int|Credis_Client decr(string $key)
|
76 |
+
* @method int|Credis_Client decrBy(string $key, int $decrement)
|
77 |
+
* @method bool|string|Credis_Client get(string $key)
|
78 |
+
* @method int|Credis_Client getBit(string $key, int $offset)
|
79 |
+
* @method string|Credis_Client getRange(string $key, int $start, int $end)
|
80 |
+
* @method string|Credis_Client getSet(string $key, string $value)
|
81 |
+
* @method int|Credis_Client incr(string $key)
|
82 |
+
* @method int|Credis_Client incrBy(string $key, int $decrement)
|
83 |
+
* @method array|Credis_Client mGet(array $keys)
|
84 |
+
* @method bool|Credis_Client mSet(array $keysValues)
|
85 |
+
* @method int|Credis_Client mSetNx(array $keysValues)
|
86 |
+
* @method bool|Credis_Client set(string $key, string $value, int | array $options = null)
|
87 |
+
* @method int|Credis_Client setBit(string $key, int $offset, int $value)
|
88 |
+
* @method bool|Credis_Client setEx(string $key, int $seconds, string $value)
|
89 |
+
* @method int|Credis_Client setNx(string $key, string $value)
|
90 |
+
* @method int |Credis_Client setRange(string $key, int $offset, int $value)
|
91 |
+
* @method int|Credis_Client strLen(string $key)
|
92 |
+
*
|
93 |
+
* Sets:
|
94 |
+
* @method int|Credis_Client sAdd(string $key, mixed $value, string $valueN = null)
|
95 |
+
* @method int|Credis_Client sRem(string $key, mixed $value, string $valueN = null)
|
96 |
+
* @method array|Credis_Client sMembers(string $key)
|
97 |
+
* @method array|Credis_Client sUnion(mixed $keyOrArray, string $valueN = null)
|
98 |
+
* @method array|Credis_Client sInter(mixed $keyOrArray, string $valueN = null)
|
99 |
+
* @method array |Credis_Client sDiff(mixed $keyOrArray, string $valueN = null)
|
100 |
+
* @method string|Credis_Client sPop(string $key)
|
101 |
+
* @method int|Credis_Client sCard(string $key)
|
102 |
+
* @method int|Credis_Client sIsMember(string $key, string $member)
|
103 |
+
* @method int|Credis_Client sMove(string $source, string $dest, string $member)
|
104 |
+
* @method string|array|Credis_Client sRandMember(string $key, int $count = null)
|
105 |
+
* @method int|Credis_Client sUnionStore(string $dest, string $key1, string $key2 = null)
|
106 |
+
* @method int|Credis_Client sInterStore(string $dest, string $key1, string $key2 = null)
|
107 |
+
* @method int|Credis_Client sDiffStore(string $dest, string $key1, string $key2 = null)
|
108 |
+
*
|
109 |
+
* Hashes:
|
110 |
+
* @method bool|int|Credis_Client hSet(string $key, string $field, string $value)
|
111 |
+
* @method bool|Credis_Client hSetNx(string $key, string $field, string $value)
|
112 |
+
* @method bool|string|Credis_Client hGet(string $key, string $field)
|
113 |
+
* @method bool|int|Credis_Client hLen(string $key)
|
114 |
+
* @method bool|Credis_Client hDel(string $key, string $field)
|
115 |
+
* @method array|Credis_Client hKeys(string $key, string $field)
|
116 |
+
* @method array|Credis_Client hVals(string $key)
|
117 |
+
* @method array|Credis_Client hGetAll(string $key)
|
118 |
+
* @method bool|Credis_Client hExists(string $key, string $field)
|
119 |
+
* @method int|Credis_Client hIncrBy(string $key, string $field, int $value)
|
120 |
+
* @method float|Credis_Client hIncrByFloat(string $key, string $member, float $value)
|
121 |
+
* @method bool|Credis_Client hMSet(string $key, array $keysValues)
|
122 |
+
* @method array|Credis_Client hMGet(string $key, array $fields)
|
123 |
+
*
|
124 |
+
* Lists:
|
125 |
+
* @method array|null|Credis_Client blPop(string $keyN, int $timeout)
|
126 |
+
* @method array|null|Credis_Client brPop(string $keyN, int $timeout)
|
127 |
+
* @method array|null |Credis_Client brPoplPush(string $source, string $destination, int $timeout)
|
128 |
+
* @method string|null|Credis_Client lIndex(string $key, int $index)
|
129 |
+
* @method int|Credis_Client lInsert(string $key, string $beforeAfter, string $pivot, string $value)
|
130 |
+
* @method int|Credis_Client lLen(string $key)
|
131 |
+
* @method string|null|Credis_Client lPop(string $key)
|
132 |
+
* @method int|Credis_Client lPush(string $key, mixed $value, mixed $valueN = null)
|
133 |
+
* @method int|Credis_Client lPushX(string $key, mixed $value)
|
134 |
+
* @method array|Credis_Client lRange(string $key, int $start, int $stop)
|
135 |
+
* @method int|Credis_Client lRem(string $key, int $count, mixed $value)
|
136 |
+
* @method bool|Credis_Client lSet(string $key, int $index, mixed $value)
|
137 |
+
* @method bool|Credis_Client lTrim(string $key, int $start, int $stop)
|
138 |
+
* @method string|null|Credis_Client rPop(string $key)
|
139 |
+
* @method string|null|Credis_Client rPoplPush(string $source, string $destination)
|
140 |
+
* @method int|Credis_Client rPush(string $key, mixed $value, mixed $valueN = null)
|
141 |
+
* @method int |Credis_Client rPushX(string $key, mixed $value)
|
142 |
+
*
|
143 |
+
* Sorted Sets:
|
144 |
+
* @method int|Credis_Client zAdd(string $key, double $score, string $value)
|
145 |
+
* @method int|Credis_Client zCard(string $key)
|
146 |
+
* @method int|Credis_Client zSize(string $key)
|
147 |
+
* @method int|Credis_Client zCount(string $key, mixed $start, mixed $stop)
|
148 |
+
* @method int|Credis_Client zIncrBy(string $key, double $value, string $member)
|
149 |
+
* @method array|Credis_Client zRangeByScore(string $key, mixed $start, mixed $stop, array $args = null)
|
150 |
+
* @method array|Credis_Client zRevRangeByScore(string $key, mixed $start, mixed $stop, array $args = null)
|
151 |
+
* @method int|Credis_Client zRemRangeByScore(string $key, mixed $start, mixed $stop)
|
152 |
+
* @method array|Credis_Client zRange(string $key, mixed $start, mixed $stop, array $args = null)
|
153 |
+
* @method array|Credis_Client zRevRange(string $key, mixed $start, mixed $stop, array $args = null)
|
154 |
+
* @method int|Credis_Client zRank(string $key, string $member)
|
155 |
+
* @method int|Credis_Client zRevRank(string $key, string $member)
|
156 |
+
* @method int|Credis_Client zRem(string $key, string $member)
|
157 |
+
* @method int|Credis_Client zDelete(string $key, string $member)
|
158 |
+
* TODO
|
159 |
+
*
|
160 |
+
* Pub/Sub
|
161 |
+
* @method int |Credis_Client publish(string $channel, string $message)
|
162 |
+
* @method int|array|Credis_Client pubsub(string $subCommand, $arg = null)
|
163 |
+
*
|
164 |
+
* Scripting:
|
165 |
+
* @method string|int|Credis_Client script(string $command, string $arg1 = null)
|
166 |
+
* @method string|int|array|bool|Credis_Client eval(string $script, array $keys = null, array $args = null)
|
167 |
+
* @method string|int|array|bool|Credis_Client evalSha(string $script, array $keys = null, array $args = null)
|
168 |
+
*/
|
169 |
+
class Credis_Client {
|
170 |
+
|
171 |
+
const VERSION = '1.11.2';
|
172 |
+
|
173 |
+
const TYPE_STRING = 'string';
|
174 |
+
const TYPE_LIST = 'list';
|
175 |
+
const TYPE_SET = 'set';
|
176 |
+
const TYPE_ZSET = 'zset';
|
177 |
+
const TYPE_HASH = 'hash';
|
178 |
+
const TYPE_NONE = 'none';
|
179 |
+
|
180 |
+
const FREAD_BLOCK_SIZE = 8192;
|
181 |
+
|
182 |
+
/**
|
183 |
+
* Socket connection to the Redis server or Redis library instance
|
184 |
+
* @var resource|Redis
|
185 |
+
*/
|
186 |
+
protected $redis;
|
187 |
+
protected $redisMulti;
|
188 |
+
|
189 |
+
/**
|
190 |
+
* Host of the Redis server
|
191 |
+
* @var string
|
192 |
+
*/
|
193 |
+
protected $host;
|
194 |
+
|
195 |
+
/**
|
196 |
+
* Scheme of the Redis server (tcp, tls, unix)
|
197 |
+
* @var string
|
198 |
+
*/
|
199 |
+
protected $scheme;
|
200 |
+
|
201 |
+
/**
|
202 |
+
* Port on which the Redis server is running
|
203 |
+
* @var integer
|
204 |
+
*/
|
205 |
+
protected $port;
|
206 |
+
|
207 |
+
/**
|
208 |
+
* Timeout for connecting to Redis server
|
209 |
+
* @var float
|
210 |
+
*/
|
211 |
+
protected $timeout;
|
212 |
+
|
213 |
+
/**
|
214 |
+
* Timeout for reading response from Redis server
|
215 |
+
* @var float
|
216 |
+
*/
|
217 |
+
protected $readTimeout;
|
218 |
+
|
219 |
+
/**
|
220 |
+
* Unique identifier for persistent connections
|
221 |
+
* @var string
|
222 |
+
*/
|
223 |
+
protected $persistent;
|
224 |
+
|
225 |
+
/**
|
226 |
+
* @var bool
|
227 |
+
*/
|
228 |
+
protected $closeOnDestruct = TRUE;
|
229 |
+
|
230 |
+
/**
|
231 |
+
* @var bool
|
232 |
+
*/
|
233 |
+
protected $connected = FALSE;
|
234 |
+
|
235 |
+
/**
|
236 |
+
* @var bool
|
237 |
+
*/
|
238 |
+
protected $standalone;
|
239 |
+
|
240 |
+
/**
|
241 |
+
* @var int
|
242 |
+
*/
|
243 |
+
protected $maxConnectRetries = 0;
|
244 |
+
|
245 |
+
/**
|
246 |
+
* @var int
|
247 |
+
*/
|
248 |
+
protected $connectFailures = 0;
|
249 |
+
|
250 |
+
/**
|
251 |
+
* @var bool
|
252 |
+
*/
|
253 |
+
protected $usePipeline = FALSE;
|
254 |
+
|
255 |
+
/**
|
256 |
+
* @var array
|
257 |
+
*/
|
258 |
+
protected $commandNames;
|
259 |
+
|
260 |
+
/**
|
261 |
+
* @var string
|
262 |
+
*/
|
263 |
+
protected $commands;
|
264 |
+
|
265 |
+
/**
|
266 |
+
* @var bool
|
267 |
+
*/
|
268 |
+
protected $isMulti = FALSE;
|
269 |
+
|
270 |
+
/**
|
271 |
+
* @var bool
|
272 |
+
*/
|
273 |
+
protected $isWatching = FALSE;
|
274 |
+
|
275 |
+
/**
|
276 |
+
* @var string
|
277 |
+
*/
|
278 |
+
protected $authPassword;
|
279 |
+
|
280 |
+
/**
|
281 |
+
* @var int
|
282 |
+
*/
|
283 |
+
protected $selectedDb = 0;
|
284 |
+
|
285 |
+
/**
|
286 |
+
* Aliases for backwards compatibility with phpredis
|
287 |
+
* @var array
|
288 |
+
*/
|
289 |
+
protected $wrapperMethods = array('delete' => 'del', 'getkeys' => 'keys', 'sremove' => 'srem');
|
290 |
+
|
291 |
+
/**
|
292 |
+
* @var array
|
293 |
+
*/
|
294 |
+
protected $renamedCommands;
|
295 |
+
|
296 |
+
/**
|
297 |
+
* @var int
|
298 |
+
*/
|
299 |
+
protected $requests = 0;
|
300 |
+
|
301 |
+
/**
|
302 |
+
* @var bool
|
303 |
+
*/
|
304 |
+
protected $subscribed = false;
|
305 |
+
|
306 |
+
|
307 |
+
/**
|
308 |
+
* Creates a Redisent connection to the Redis server on host {@link $host} and port {@link $port}.
|
309 |
+
* $host may also be a path to a unix socket or a string in the form of tcp://[hostname]:[port] or unix://[path]
|
310 |
+
*
|
311 |
+
* @param string $host The hostname of the Redis server
|
312 |
+
* @param integer $port The port number of the Redis server
|
313 |
+
* @param float $timeout Timeout period in seconds
|
314 |
+
* @param string $persistent Flag to establish persistent connection
|
315 |
+
* @param int $db The selected datbase of the Redis server
|
316 |
+
* @param string $password The authentication password of the Redis server
|
317 |
+
*/
|
318 |
+
public function __construct($host = '127.0.0.1', $port = 6379, $timeout = null, $persistent = '', $db = 0, $password = null)
|
319 |
+
{
|
320 |
+
$this->host = (string) $host;
|
321 |
+
$this->port = (int) $port;
|
322 |
+
$this->scheme = null;
|
323 |
+
$this->timeout = $timeout;
|
324 |
+
$this->persistent = (string) $persistent;
|
325 |
+
$this->standalone = ! extension_loaded('redis');
|
326 |
+
$this->authPassword = $password;
|
327 |
+
$this->selectedDb = (int)$db;
|
328 |
+
$this->convertHost();
|
329 |
+
if ($this->scheme == 'tls') {
|
330 |
+
// PHP Redis extension doesn't work with TLS
|
331 |
+
$this->standalone = true;
|
332 |
+
}
|
333 |
+
}
|
334 |
+
|
335 |
+
public function __destruct()
|
336 |
+
{
|
337 |
+
if ($this->closeOnDestruct) {
|
338 |
+
$this->close();
|
339 |
+
}
|
340 |
+
}
|
341 |
+
|
342 |
+
/**
|
343 |
+
* @return bool
|
344 |
+
*/
|
345 |
+
public function isSubscribed()
|
346 |
+
{
|
347 |
+
return $this->subscribed;
|
348 |
+
}
|
349 |
+
|
350 |
+
/**
|
351 |
+
* Return the host of the Redis instance
|
352 |
+
* @return string
|
353 |
+
*/
|
354 |
+
public function getHost()
|
355 |
+
{
|
356 |
+
return $this->host;
|
357 |
+
}
|
358 |
+
/**
|
359 |
+
* Return the port of the Redis instance
|
360 |
+
* @return int
|
361 |
+
*/
|
362 |
+
public function getPort()
|
363 |
+
{
|
364 |
+
return $this->port;
|
365 |
+
}
|
366 |
+
|
367 |
+
/**
|
368 |
+
* Return the selected database
|
369 |
+
* @return int
|
370 |
+
*/
|
371 |
+
public function getSelectedDb()
|
372 |
+
{
|
373 |
+
return $this->selectedDb;
|
374 |
+
}
|
375 |
+
/**
|
376 |
+
* @return string
|
377 |
+
*/
|
378 |
+
public function getPersistence()
|
379 |
+
{
|
380 |
+
return $this->persistent;
|
381 |
+
}
|
382 |
+
/**
|
383 |
+
* @throws CredisException
|
384 |
+
* @return Credis_Client
|
385 |
+
*/
|
386 |
+
public function forceStandalone()
|
387 |
+
{
|
388 |
+
if ($this->standalone) {
|
389 |
+
return $this;
|
390 |
+
}
|
391 |
+
if($this->connected) {
|
392 |
+
throw new CredisException('Cannot force Credis_Client to use standalone PHP driver after a connection has already been established.');
|
393 |
+
}
|
394 |
+
$this->standalone = TRUE;
|
395 |
+
return $this;
|
396 |
+
}
|
397 |
+
|
398 |
+
/**
|
399 |
+
* @param int $retries
|
400 |
+
* @return Credis_Client
|
401 |
+
*/
|
402 |
+
public function setMaxConnectRetries($retries)
|
403 |
+
{
|
404 |
+
$this->maxConnectRetries = $retries;
|
405 |
+
return $this;
|
406 |
+
}
|
407 |
+
|
408 |
+
/**
|
409 |
+
* @param bool $flag
|
410 |
+
* @return Credis_Client
|
411 |
+
*/
|
412 |
+
public function setCloseOnDestruct($flag)
|
413 |
+
{
|
414 |
+
$this->closeOnDestruct = $flag;
|
415 |
+
return $this;
|
416 |
+
}
|
417 |
+
protected function convertHost()
|
418 |
+
{
|
419 |
+
if (preg_match('#^(tcp|tls|unix)://(.*)$#', $this->host, $matches)) {
|
420 |
+
if($matches[1] == 'tcp' || $matches[1] == 'tls') {
|
421 |
+
$this->scheme = $matches[1];
|
422 |
+
if ( ! preg_match('#^([^:]+)(:([0-9]+))?(/(.+))?$#', $matches[2], $matches)) {
|
423 |
+
throw new CredisException('Invalid host format; expected '.$this->scheme.'://host[:port][/persistence_identifier]');
|
424 |
+
}
|
425 |
+
$this->host = $matches[1];
|
426 |
+
$this->port = (int) (isset($matches[3]) ? $matches[3] : 6379);
|
427 |
+
$this->persistent = isset($matches[5]) ? $matches[5] : '';
|
428 |
+
} else {
|
429 |
+
$this->host = $matches[2];
|
430 |
+
$this->port = NULL;
|
431 |
+
$this->scheme = 'unix';
|
432 |
+
if (substr($this->host,0,1) != '/') {
|
433 |
+
throw new CredisException('Invalid unix socket format; expected unix:///path/to/redis.sock');
|
434 |
+
}
|
435 |
+
}
|
436 |
+
}
|
437 |
+
if ($this->port !== NULL && substr($this->host,0,1) == '/') {
|
438 |
+
$this->port = NULL;
|
439 |
+
$this->scheme = 'unix';
|
440 |
+
}
|
441 |
+
if (!$this->scheme) {
|
442 |
+
$this->scheme = 'tcp';
|
443 |
+
}
|
444 |
+
}
|
445 |
+
/**
|
446 |
+
* @throws CredisException
|
447 |
+
* @return Credis_Client
|
448 |
+
*/
|
449 |
+
public function connect()
|
450 |
+
{
|
451 |
+
if ($this->connected) {
|
452 |
+
return $this;
|
453 |
+
}
|
454 |
+
$this->close(true);
|
455 |
+
|
456 |
+
if ($this->standalone) {
|
457 |
+
$flags = STREAM_CLIENT_CONNECT;
|
458 |
+
$remote_socket = $this->port === NULL
|
459 |
+
? $this->scheme.'://'.$this->host
|
460 |
+
: $this->scheme.'://'.$this->host.':'.$this->port;
|
461 |
+
if ($this->persistent && $this->port !== NULL) {
|
462 |
+
// Persistent connections to UNIX sockets are not supported
|
463 |
+
$remote_socket .= '/'.$this->persistent;
|
464 |
+
$flags = $flags | STREAM_CLIENT_PERSISTENT;
|
465 |
+
}
|
466 |
+
$result = $this->redis = @stream_socket_client($remote_socket, $errno, $errstr, $this->timeout !== null ? $this->timeout : 2.5, $flags);
|
467 |
+
}
|
468 |
+
else {
|
469 |
+
if ( ! $this->redis) {
|
470 |
+
$this->redis = new Redis;
|
471 |
+
}
|
472 |
+
$socketTimeout = $this->timeout ? $this->timeout : 0.0;
|
473 |
+
try
|
474 |
+
{
|
475 |
+
$result = $this->persistent
|
476 |
+
? $this->redis->pconnect($this->host, $this->port, $socketTimeout, $this->persistent)
|
477 |
+
: $this->redis->connect($this->host, $this->port, $socketTimeout);
|
478 |
+
}
|
479 |
+
catch(Exception $e)
|
480 |
+
{
|
481 |
+
// Some applications will capture the php error that phpredis can sometimes generate and throw it as an Exception
|
482 |
+
$result = false;
|
483 |
+
$errno = 1;
|
484 |
+
$errstr = $e->getMessage();
|
485 |
+
}
|
486 |
+
}
|
487 |
+
|
488 |
+
// Use recursion for connection retries
|
489 |
+
if ( ! $result) {
|
490 |
+
$this->connectFailures++;
|
491 |
+
if ($this->connectFailures <= $this->maxConnectRetries) {
|
492 |
+
return $this->connect();
|
493 |
+
}
|
494 |
+
$failures = $this->connectFailures;
|
495 |
+
$this->connectFailures = 0;
|
496 |
+
throw new CredisException("Connection to Redis {$this->host}:{$this->port} failed after $failures failures." . (isset($errno) && isset($errstr) ? "Last Error : ({$errno}) {$errstr}" : ""));
|
497 |
+
}
|
498 |
+
|
499 |
+
$this->connectFailures = 0;
|
500 |
+
$this->connected = TRUE;
|
501 |
+
|
502 |
+
// Set read timeout
|
503 |
+
if ($this->readTimeout) {
|
504 |
+
$this->setReadTimeout($this->readTimeout);
|
505 |
+
}
|
506 |
+
|
507 |
+
if($this->authPassword) {
|
508 |
+
$this->auth($this->authPassword);
|
509 |
+
}
|
510 |
+
if($this->selectedDb !== 0) {
|
511 |
+
$this->select($this->selectedDb);
|
512 |
+
}
|
513 |
+
return $this;
|
514 |
+
}
|
515 |
+
/**
|
516 |
+
* @return bool
|
517 |
+
*/
|
518 |
+
public function isConnected()
|
519 |
+
{
|
520 |
+
return $this->connected;
|
521 |
+
}
|
522 |
+
/**
|
523 |
+
* Set the read timeout for the connection. Use 0 to disable timeouts entirely (or use a very long timeout
|
524 |
+
* if not supported).
|
525 |
+
*
|
526 |
+
* @param int $timeout 0 (or -1) for no timeout, otherwise number of seconds
|
527 |
+
* @throws CredisException
|
528 |
+
* @return Credis_Client
|
529 |
+
*/
|
530 |
+
public function setReadTimeout($timeout)
|
531 |
+
{
|
532 |
+
if ($timeout < -1) {
|
533 |
+
throw new CredisException('Timeout values less than -1 are not accepted.');
|
534 |
+
}
|
535 |
+
$this->readTimeout = $timeout;
|
536 |
+
if ($this->isConnected()) {
|
537 |
+
if ($this->standalone) {
|
538 |
+
$timeout = $timeout <= 0 ? 315360000 : $timeout; // Ten-year timeout
|
539 |
+
stream_set_blocking($this->redis, TRUE);
|
540 |
+
stream_set_timeout($this->redis, (int) floor($timeout), ($timeout - floor($timeout)) * 1000000);
|
541 |
+
} else if (defined('Redis::OPT_READ_TIMEOUT')) {
|
542 |
+
// supported in phpredis 2.2.3
|
543 |
+
// a timeout value of -1 means reads will not timeout
|
544 |
+
$timeout = $timeout == 0 ? -1 : $timeout;
|
545 |
+
$this->redis->setOption(Redis::OPT_READ_TIMEOUT, $timeout);
|
546 |
+
}
|
547 |
+
}
|
548 |
+
return $this;
|
549 |
+
}
|
550 |
+
|
551 |
+
/**
|
552 |
+
* @return bool
|
553 |
+
*/
|
554 |
+
public function close($force = FALSE)
|
555 |
+
{
|
556 |
+
$result = TRUE;
|
557 |
+
if ($this->redis && ($force || $this->connected && ! $this->persistent)) {
|
558 |
+
try {
|
559 |
+
if (is_callable(array($this->redis, 'close'))) {
|
560 |
+
$this->redis->close();
|
561 |
+
} else {
|
562 |
+
@fclose($this->redis);
|
563 |
+
$this->redis = null;
|
564 |
+
}
|
565 |
+
} catch (Exception $e) {
|
566 |
+
; // Ignore exceptions on close
|
567 |
+
}
|
568 |
+
$this->connected = $this->usePipeline = $this->isMulti = $this->isWatching = FALSE;
|
569 |
+
}
|
570 |
+
return $result;
|
571 |
+
}
|
572 |
+
|
573 |
+
/**
|
574 |
+
* Enabled command renaming and provide mapping method. Supported methods are:
|
575 |
+
*
|
576 |
+
* 1. renameCommand('foo') // Salted md5 hash for all commands -> md5('foo'.$command)
|
577 |
+
* 2. renameCommand(function($command){ return 'my'.$command; }); // Callable
|
578 |
+
* 3. renameCommand('get', 'foo') // Single command -> alias
|
579 |
+
* 4. renameCommand(['get' => 'foo', 'set' => 'bar']) // Full map of [command -> alias]
|
580 |
+
*
|
581 |
+
* @param string|callable|array $command
|
582 |
+
* @param string|null $alias
|
583 |
+
* @return $this
|
584 |
+
*/
|
585 |
+
public function renameCommand($command, $alias = NULL)
|
586 |
+
{
|
587 |
+
if ( ! $this->standalone) {
|
588 |
+
$this->forceStandalone();
|
589 |
+
}
|
590 |
+
if ($alias === NULL) {
|
591 |
+
$this->renamedCommands = $command;
|
592 |
+
} else {
|
593 |
+
if ( ! $this->renamedCommands) {
|
594 |
+
$this->renamedCommands = array();
|
595 |
+
}
|
596 |
+
$this->renamedCommands[$command] = $alias;
|
597 |
+
}
|
598 |
+
return $this;
|
599 |
+
}
|
600 |
+
|
601 |
+
/**
|
602 |
+
* @param $command
|
603 |
+
* @return string
|
604 |
+
*/
|
605 |
+
public function getRenamedCommand($command)
|
606 |
+
{
|
607 |
+
static $map;
|
608 |
+
|
609 |
+
// Command renaming not enabled
|
610 |
+
if ($this->renamedCommands === NULL) {
|
611 |
+
return $command;
|
612 |
+
}
|
613 |
+
|
614 |
+
// Initialize command map
|
615 |
+
if ($map === NULL) {
|
616 |
+
if (is_array($this->renamedCommands)) {
|
617 |
+
$map = $this->renamedCommands;
|
618 |
+
} else {
|
619 |
+
$map = array();
|
620 |
+
}
|
621 |
+
}
|
622 |
+
|
623 |
+
// Generate and return cached result
|
624 |
+
if ( ! isset($map[$command])) {
|
625 |
+
// String means all commands are hashed with salted md5
|
626 |
+
if (is_string($this->renamedCommands)) {
|
627 |
+
$map[$command] = md5($this->renamedCommands.$command);
|
628 |
+
}
|
629 |
+
// Would already be set in $map if it was intended to be renamed
|
630 |
+
else if (is_array($this->renamedCommands)) {
|
631 |
+
return $command;
|
632 |
+
}
|
633 |
+
// User-supplied function
|
634 |
+
else if (is_callable($this->renamedCommands)) {
|
635 |
+
$map[$command] = call_user_func($this->renamedCommands, $command);
|
636 |
+
}
|
637 |
+
}
|
638 |
+
return $map[$command];
|
639 |
+
}
|
640 |
+
|
641 |
+
/**
|
642 |
+
* @param string $password
|
643 |
+
* @return bool
|
644 |
+
*/
|
645 |
+
public function auth($password)
|
646 |
+
{
|
647 |
+
$response = $this->__call('auth', array($password));
|
648 |
+
$this->authPassword = $password;
|
649 |
+
return $response;
|
650 |
+
}
|
651 |
+
|
652 |
+
/**
|
653 |
+
* @param int $index
|
654 |
+
* @return bool
|
655 |
+
*/
|
656 |
+
public function select($index)
|
657 |
+
{
|
658 |
+
$response = $this->__call('select', array($index));
|
659 |
+
$this->selectedDb = (int) $index;
|
660 |
+
return $response;
|
661 |
+
}
|
662 |
+
|
663 |
+
/**
|
664 |
+
* @param string|array $pattern
|
665 |
+
* @return array
|
666 |
+
*/
|
667 |
+
public function pUnsubscribe()
|
668 |
+
{
|
669 |
+
list($command, $channel, $subscribedChannels) = $this->__call('punsubscribe', func_get_args());
|
670 |
+
$this->subscribed = $subscribedChannels > 0;
|
671 |
+
return array($command, $channel, $subscribedChannels);
|
672 |
+
}
|
673 |
+
|
674 |
+
/**
|
675 |
+
* @param int $Iterator
|
676 |
+
* @param string $pattern
|
677 |
+
* @param int $count
|
678 |
+
* @return bool|array
|
679 |
+
*/
|
680 |
+
public function scan(&$Iterator, $pattern = null, $count = null)
|
681 |
+
{
|
682 |
+
return $this->__call('scan', array(&$Iterator, $pattern, $count));
|
683 |
+
}
|
684 |
+
|
685 |
+
/**
|
686 |
+
* @param int $Iterator
|
687 |
+
* @param string $field
|
688 |
+
* @param string $pattern
|
689 |
+
* @param int $count
|
690 |
+
* @return bool|array
|
691 |
+
*/
|
692 |
+
public function hscan(&$Iterator, $field, $pattern = null, $count = null)
|
693 |
+
{
|
694 |
+
return $this->__call('hscan', array($field, &$Iterator, $pattern, $count));
|
695 |
+
}
|
696 |
+
|
697 |
+
/**
|
698 |
+
* @param int $Iterator
|
699 |
+
* @param string $field
|
700 |
+
* @param string $pattern
|
701 |
+
* @param int $Iterator
|
702 |
+
* @return bool|array
|
703 |
+
*/
|
704 |
+
public function sscan(&$Iterator, $field, $pattern = null, $count = null)
|
705 |
+
{
|
706 |
+
return $this->__call('sscan', array($field, &$Iterator, $pattern, $count));
|
707 |
+
}
|
708 |
+
|
709 |
+
/**
|
710 |
+
* @param int $Iterator
|
711 |
+
* @param string $field
|
712 |
+
* @param string $pattern
|
713 |
+
* @param int $Iterator
|
714 |
+
* @return bool|array
|
715 |
+
*/
|
716 |
+
public function zscan(&$Iterator, $field, $pattern = null, $count = null)
|
717 |
+
{
|
718 |
+
return $this->__call('zscan', array($field, &$Iterator, $pattern, $count));
|
719 |
+
}
|
720 |
+
|
721 |
+
/**
|
722 |
+
* @param string|array $patterns
|
723 |
+
* @param $callback
|
724 |
+
* @return $this|array|bool|Credis_Client|mixed|null|string
|
725 |
+
* @throws CredisException
|
726 |
+
*/
|
727 |
+
public function pSubscribe($patterns, $callback)
|
728 |
+
{
|
729 |
+
if ( ! $this->standalone) {
|
730 |
+
return $this->__call('pSubscribe', array((array)$patterns, $callback));
|
731 |
+
}
|
732 |
+
|
733 |
+
// Standalone mode: use infinite loop to subscribe until timeout
|
734 |
+
$patternCount = is_array($patterns) ? count($patterns) : 1;
|
735 |
+
while ($patternCount--) {
|
736 |
+
if (isset($status)) {
|
737 |
+
list($command, $pattern, $status) = $this->read_reply();
|
738 |
+
} else {
|
739 |
+
list($command, $pattern, $status) = $this->__call('psubscribe', array($patterns));
|
740 |
+
}
|
741 |
+
$this->subscribed = $status > 0;
|
742 |
+
if ( ! $status) {
|
743 |
+
throw new CredisException('Invalid pSubscribe response.');
|
744 |
+
}
|
745 |
+
}
|
746 |
+
while ($this->subscribed) {
|
747 |
+
list($type, $pattern, $channel, $message) = $this->read_reply();
|
748 |
+
if ($type != 'pmessage') {
|
749 |
+
throw new CredisException('Received non-pmessage reply.');
|
750 |
+
}
|
751 |
+
$callback($this, $pattern, $channel, $message);
|
752 |
+
}
|
753 |
+
return null;
|
754 |
+
}
|
755 |
+
|
756 |
+
/**
|
757 |
+
* @param string|array $pattern
|
758 |
+
* @return array
|
759 |
+
*/
|
760 |
+
public function unsubscribe()
|
761 |
+
{
|
762 |
+
list($command, $channel, $subscribedChannels) = $this->__call('unsubscribe', func_get_args());
|
763 |
+
$this->subscribed = $subscribedChannels > 0;
|
764 |
+
return array($command, $channel, $subscribedChannels);
|
765 |
+
}
|
766 |
+
|
767 |
+
/**
|
768 |
+
* @param string|array $channels
|
769 |
+
* @param $callback
|
770 |
+
* @throws CredisException
|
771 |
+
* @return $this|array|bool|Credis_Client|mixed|null|string
|
772 |
+
*/
|
773 |
+
public function subscribe($channels, $callback)
|
774 |
+
{
|
775 |
+
if ( ! $this->standalone) {
|
776 |
+
return $this->__call('subscribe', array((array)$channels, $callback));
|
777 |
+
}
|
778 |
+
|
779 |
+
// Standalone mode: use infinite loop to subscribe until timeout
|
780 |
+
$channelCount = is_array($channels) ? count($channels) : 1;
|
781 |
+
while ($channelCount--) {
|
782 |
+
if (isset($status)) {
|
783 |
+
list($command, $channel, $status) = $this->read_reply();
|
784 |
+
} else {
|
785 |
+
list($command, $channel, $status) = $this->__call('subscribe', array($channels));
|
786 |
+
}
|
787 |
+
$this->subscribed = $status > 0;
|
788 |
+
if ( ! $status) {
|
789 |
+
throw new CredisException('Invalid subscribe response.');
|
790 |
+
}
|
791 |
+
}
|
792 |
+
while ($this->subscribed) {
|
793 |
+
list($type, $channel, $message) = $this->read_reply();
|
794 |
+
if ($type != 'message') {
|
795 |
+
throw new CredisException('Received non-message reply.');
|
796 |
+
}
|
797 |
+
$callback($this, $channel, $message);
|
798 |
+
}
|
799 |
+
return null;
|
800 |
+
}
|
801 |
+
|
802 |
+
/**
|
803 |
+
* @param string|null $name
|
804 |
+
* @return string|Credis_Client
|
805 |
+
*/
|
806 |
+
public function ping($name = null)
|
807 |
+
{
|
808 |
+
return $this->__call('ping', $name ? array($name) : array());
|
809 |
+
}
|
810 |
+
|
811 |
+
public function __call($name, $args)
|
812 |
+
{
|
813 |
+
// Lazy connection
|
814 |
+
$this->connect();
|
815 |
+
|
816 |
+
$name = strtolower($name);
|
817 |
+
|
818 |
+
// Send request via native PHP
|
819 |
+
if($this->standalone)
|
820 |
+
{
|
821 |
+
$trackedArgs = array();
|
822 |
+
switch ($name) {
|
823 |
+
case 'eval':
|
824 |
+
case 'evalsha':
|
825 |
+
$script = array_shift($args);
|
826 |
+
$keys = (array) array_shift($args);
|
827 |
+
$eArgs = (array) array_shift($args);
|
828 |
+
$args = array($script, count($keys), $keys, $eArgs);
|
829 |
+
break;
|
830 |
+
case 'zinterstore':
|
831 |
+
case 'zunionstore':
|
832 |
+
$dest = array_shift($args);
|
833 |
+
$keys = (array) array_shift($args);
|
834 |
+
$weights = array_shift($args);
|
835 |
+
$aggregate = array_shift($args);
|
836 |
+
$args = array($dest, count($keys), $keys);
|
837 |
+
if ($weights) {
|
838 |
+
$args[] = (array) $weights;
|
839 |
+
}
|
840 |
+
if ($aggregate) {
|
841 |
+
$args[] = $aggregate;
|
842 |
+
}
|
843 |
+
break;
|
844 |
+
case 'set':
|
845 |
+
// The php redis module has different behaviour with ttl
|
846 |
+
// https://github.com/phpredis/phpredis#set
|
847 |
+
if (count($args) === 3 && is_int($args[2])) {
|
848 |
+
$args = array($args[0], $args[1], array('EX', $args[2]));
|
849 |
+
} elseif (count($args) === 3 && is_array($args[2])) {
|
850 |
+
$tmp_args = $args;
|
851 |
+
$args = array($tmp_args[0], $tmp_args[1]);
|
852 |
+
foreach ($tmp_args[2] as $k=>$v) {
|
853 |
+
if (is_string($k)) {
|
854 |
+
$args[] = array($k,$v);
|
855 |
+
} elseif (is_int($k)) {
|
856 |
+
$args[] = $v;
|
857 |
+
}
|
858 |
+
}
|
859 |
+
unset($tmp_args);
|
860 |
+
}
|
861 |
+
break;
|
862 |
+
case 'scan':
|
863 |
+
$trackedArgs = array(&$args[0]);
|
864 |
+
if (empty($trackedArgs[0]))
|
865 |
+
{
|
866 |
+
$trackedArgs[0] = 0;
|
867 |
+
}
|
868 |
+
$eArgs = array($trackedArgs[0]);
|
869 |
+
if (!empty($args[1]))
|
870 |
+
{
|
871 |
+
$eArgs[] = 'MATCH';
|
872 |
+
$eArgs[] = $args[1];
|
873 |
+
}
|
874 |
+
if (!empty($args[2]))
|
875 |
+
{
|
876 |
+
$eArgs[] = 'COUNT';
|
877 |
+
$eArgs[] = $args[2];
|
878 |
+
}
|
879 |
+
$args = $eArgs;
|
880 |
+
break;
|
881 |
+
case 'sscan':
|
882 |
+
case 'zscan':
|
883 |
+
case 'hscan':
|
884 |
+
$trackedArgs = array(&$args[1]);
|
885 |
+
if (empty($trackedArgs[0]))
|
886 |
+
{
|
887 |
+
$trackedArgs[0] = 0;
|
888 |
+
}
|
889 |
+
$eArgs = array($args[0],$trackedArgs[0]);
|
890 |
+
if (!empty($args[2]))
|
891 |
+
{
|
892 |
+
$eArgs[] = 'MATCH';
|
893 |
+
$eArgs[] = $args[2];
|
894 |
+
}
|
895 |
+
if (!empty($args[3]))
|
896 |
+
{
|
897 |
+
$eArgs[] = 'COUNT';
|
898 |
+
$eArgs[] = $args[3];
|
899 |
+
}
|
900 |
+
$args = $eArgs;
|
901 |
+
break;
|
902 |
+
case 'zrangebyscore':
|
903 |
+
case 'zrevrangebyscore':
|
904 |
+
case 'zrange':
|
905 |
+
case 'zrevrange':
|
906 |
+
if (isset($args[3]) && is_array($args[3])) {
|
907 |
+
// map options
|
908 |
+
$cArgs = array();
|
909 |
+
if (!empty($args[3]['withscores'])) {
|
910 |
+
$cArgs[] = 'withscores';
|
911 |
+
}
|
912 |
+
if (($name == 'zrangebyscore' || $name == 'zrevrangebyscore') && array_key_exists('limit', $args[3])) {
|
913 |
+
$cArgs[] = array('limit' => $args[3]['limit']);
|
914 |
+
}
|
915 |
+
$args[3] = $cArgs;
|
916 |
+
$trackedArgs = $cArgs;
|
917 |
+
}
|
918 |
+
break;
|
919 |
+
case 'mget':
|
920 |
+
if (isset($args[0]) && is_array($args[0]))
|
921 |
+
{
|
922 |
+
$args = array_values($args[0]);
|
923 |
+
}
|
924 |
+
break;
|
925 |
+
case 'hmset':
|
926 |
+
if (isset($args[1]) && is_array($args[1]))
|
927 |
+
{
|
928 |
+
$cArgs = array();
|
929 |
+
foreach($args[1] as $id => $value)
|
930 |
+
{
|
931 |
+
$cArgs[] = $id;
|
932 |
+
$cArgs[] = $value;
|
933 |
+
}
|
934 |
+
$args[1] = $cArgs;
|
935 |
+
}
|
936 |
+
break;
|
937 |
+
case 'zsize':
|
938 |
+
$name = 'zcard';
|
939 |
+
break;
|
940 |
+
case 'zdelete':
|
941 |
+
$name = 'zrem';
|
942 |
+
break;
|
943 |
+
case 'hmget':
|
944 |
+
// hmget needs to track the keys for rehydrating the results
|
945 |
+
if (isset($args[1]))
|
946 |
+
{
|
947 |
+
$trackedArgs = $args[1];
|
948 |
+
}
|
949 |
+
break;
|
950 |
+
}
|
951 |
+
// Flatten arguments
|
952 |
+
$args = self::_flattenArguments($args);
|
953 |
+
|
954 |
+
// In pipeline mode
|
955 |
+
if($this->usePipeline)
|
956 |
+
{
|
957 |
+
if($name === 'pipeline') {
|
958 |
+
throw new CredisException('A pipeline is already in use and only one pipeline is supported.');
|
959 |
+
}
|
960 |
+
else if($name === 'exec') {
|
961 |
+
if($this->isMulti) {
|
962 |
+
$this->commandNames[] = array($name, $trackedArgs);
|
963 |
+
$this->commands .= self::_prepare_command(array($this->getRenamedCommand($name)));
|
964 |
+
}
|
965 |
+
|
966 |
+
// Write request
|
967 |
+
if($this->commands) {
|
968 |
+
$this->write_command($this->commands);
|
969 |
+
}
|
970 |
+
$this->commands = NULL;
|
971 |
+
|
972 |
+
// Read response
|
973 |
+
$queuedResponses = array();
|
974 |
+
$response = array();
|
975 |
+
foreach($this->commandNames as $command) {
|
976 |
+
list($name, $arguments) = $command;
|
977 |
+
$result = $this->read_reply($name, true);
|
978 |
+
if ($result !== null)
|
979 |
+
{
|
980 |
+
$result = $this->decode_reply($name, $result, $arguments);
|
981 |
+
}
|
982 |
+
else
|
983 |
+
{
|
984 |
+
$queuedResponses[] = $command;
|
985 |
+
}
|
986 |
+
$response[] = $result;
|
987 |
+
}
|
988 |
+
|
989 |
+
if($this->isMulti) {
|
990 |
+
$response = array_pop($response);
|
991 |
+
foreach($queuedResponses as $key => $command)
|
992 |
+
{
|
993 |
+
list($name, $arguments) = $command;
|
994 |
+
$response[$key] = $this->decode_reply($name, $response[$key], $arguments);
|
995 |
+
}
|
996 |
+
}
|
997 |
+
|
998 |
+
$this->commandNames = NULL;
|
999 |
+
$this->usePipeline = $this->isMulti = FALSE;
|
1000 |
+
return $response;
|
1001 |
+
}
|
1002 |
+
else if ($name === 'discard')
|
1003 |
+
{
|
1004 |
+
$this->commands = NULL;
|
1005 |
+
$this->commandNames = NULL;
|
1006 |
+
$this->usePipeline = $this->isMulti = FALSE;
|
1007 |
+
}
|
1008 |
+
else {
|
1009 |
+
if($name === 'multi') {
|
1010 |
+
$this->isMulti = TRUE;
|
1011 |
+
}
|
1012 |
+
array_unshift($args, $this->getRenamedCommand($name));
|
1013 |
+
$this->commandNames[] = array($name, $trackedArgs);
|
1014 |
+
$this->commands .= self::_prepare_command($args);
|
1015 |
+
return $this;
|
1016 |
+
}
|
1017 |
+
}
|
1018 |
+
|
1019 |
+
// Start pipeline mode
|
1020 |
+
if($name === 'pipeline')
|
1021 |
+
{
|
1022 |
+
$this->usePipeline = TRUE;
|
1023 |
+
$this->commandNames = array();
|
1024 |
+
$this->commands = '';
|
1025 |
+
return $this;
|
1026 |
+
}
|
1027 |
+
|
1028 |
+
// If unwatching, allow reconnect with no error thrown
|
1029 |
+
if($name === 'unwatch') {
|
1030 |
+
$this->isWatching = FALSE;
|
1031 |
+
}
|
1032 |
+
|
1033 |
+
// Non-pipeline mode
|
1034 |
+
array_unshift($args, $this->getRenamedCommand($name));
|
1035 |
+
$command = self::_prepare_command($args);
|
1036 |
+
$this->write_command($command);
|
1037 |
+
$response = $this->read_reply($name);
|
1038 |
+
$response = $this->decode_reply($name, $response, $trackedArgs);
|
1039 |
+
|
1040 |
+
// Watch mode disables reconnect so error is thrown
|
1041 |
+
if($name == 'watch') {
|
1042 |
+
$this->isWatching = TRUE;
|
1043 |
+
}
|
1044 |
+
// Transaction mode
|
1045 |
+
else if($this->isMulti && ($name == 'exec' || $name == 'discard')) {
|
1046 |
+
$this->isMulti = FALSE;
|
1047 |
+
}
|
1048 |
+
// Started transaction
|
1049 |
+
else if($this->isMulti || $name == 'multi') {
|
1050 |
+
$this->isMulti = TRUE;
|
1051 |
+
$response = $this;
|
1052 |
+
}
|
1053 |
+
}
|
1054 |
+
|
1055 |
+
// Send request via phpredis client
|
1056 |
+
else
|
1057 |
+
{
|
1058 |
+
// Tweak arguments
|
1059 |
+
switch($name) {
|
1060 |
+
case 'get': // optimize common cases
|
1061 |
+
case 'set':
|
1062 |
+
case 'hget':
|
1063 |
+
case 'hset':
|
1064 |
+
case 'setex':
|
1065 |
+
case 'mset':
|
1066 |
+
case 'msetnx':
|
1067 |
+
case 'hmset':
|
1068 |
+
case 'hmget':
|
1069 |
+
case 'del':
|
1070 |
+
case 'zrangebyscore':
|
1071 |
+
case 'zrevrangebyscore':
|
1072 |
+
break;
|
1073 |
+
case 'zrange':
|
1074 |
+
case 'zrevrange':
|
1075 |
+
if (isset($args[3]) && is_array($args[3]))
|
1076 |
+
{
|
1077 |
+
$cArgs = $args[3];
|
1078 |
+
$args[3] = !empty($cArgs['withscores']);
|
1079 |
+
}
|
1080 |
+
$args = self::_flattenArguments($args);
|
1081 |
+
break;
|
1082 |
+
case 'zinterstore':
|
1083 |
+
case 'zunionstore':
|
1084 |
+
$cArgs = array();
|
1085 |
+
$cArgs[] = array_shift($args); // destination
|
1086 |
+
$cArgs[] = array_shift($args); // keys
|
1087 |
+
if(isset($args[0]) and isset($args[0]['weights'])) {
|
1088 |
+
$cArgs[] = (array) $args[0]['weights'];
|
1089 |
+
} else {
|
1090 |
+
$cArgs[] = null;
|
1091 |
+
}
|
1092 |
+
if(isset($args[0]) and isset($args[0]['aggregate'])) {
|
1093 |
+
$cArgs[] = strtoupper($args[0]['aggregate']);
|
1094 |
+
}
|
1095 |
+
$args = $cArgs;
|
1096 |
+
break;
|
1097 |
+
case 'mget':
|
1098 |
+
if(isset($args[0]) && ! is_array($args[0])) {
|
1099 |
+
$args = array($args);
|
1100 |
+
}
|
1101 |
+
break;
|
1102 |
+
case 'lrem':
|
1103 |
+
$args = array($args[0], $args[2], $args[1]);
|
1104 |
+
break;
|
1105 |
+
case 'eval':
|
1106 |
+
case 'evalsha':
|
1107 |
+
if (isset($args[1]) && is_array($args[1])) {
|
1108 |
+
$cKeys = $args[1];
|
1109 |
+
} elseif (isset($args[1]) && is_string($args[1])) {
|
1110 |
+
$cKeys = array($args[1]);
|
1111 |
+
} else {
|
1112 |
+
$cKeys = array();
|
1113 |
+
}
|
1114 |
+
if (isset($args[2]) && is_array($args[2])) {
|
1115 |
+
$cArgs = $args[2];
|
1116 |
+
} elseif (isset($args[2]) && is_string($args[2])) {
|
1117 |
+
$cArgs = array($args[2]);
|
1118 |
+
} else {
|
1119 |
+
$cArgs = array();
|
1120 |
+
}
|
1121 |
+
$args = array($args[0], array_merge($cKeys, $cArgs), count($cKeys));
|
1122 |
+
break;
|
1123 |
+
case 'subscribe':
|
1124 |
+
case 'psubscribe':
|
1125 |
+
break;
|
1126 |
+
case 'scan':
|
1127 |
+
case 'sscan':
|
1128 |
+
case 'hscan':
|
1129 |
+
case 'zscan':
|
1130 |
+
// allow phpredis to see the caller's reference
|
1131 |
+
//$param_ref =& $args[0];
|
1132 |
+
break;
|
1133 |
+
default:
|
1134 |
+
// Flatten arguments
|
1135 |
+
$args = self::_flattenArguments($args);
|
1136 |
+
}
|
1137 |
+
|
1138 |
+
try {
|
1139 |
+
// Proxy pipeline mode to the phpredis library
|
1140 |
+
if($name == 'pipeline' || $name == 'multi') {
|
1141 |
+
if($this->isMulti) {
|
1142 |
+
return $this;
|
1143 |
+
} else {
|
1144 |
+
$this->isMulti = TRUE;
|
1145 |
+
$this->redisMulti = call_user_func_array(array($this->redis, $name), $args);
|
1146 |
+
return $this;
|
1147 |
+
}
|
1148 |
+
}
|
1149 |
+
else if($name == 'exec' || $name == 'discard') {
|
1150 |
+
$this->isMulti = FALSE;
|
1151 |
+
$response = $this->redisMulti->$name();
|
1152 |
+
$this->redisMulti = NULL;
|
1153 |
+
#echo "> $name : ".substr(print_r($response, TRUE),0,100)."\n";
|
1154 |
+
return $response;
|
1155 |
+
}
|
1156 |
+
|
1157 |
+
// Use aliases to be compatible with phpredis wrapper
|
1158 |
+
if(isset($this->wrapperMethods[$name])) {
|
1159 |
+
$name = $this->wrapperMethods[$name];
|
1160 |
+
}
|
1161 |
+
|
1162 |
+
// Multi and pipeline return self for chaining
|
1163 |
+
if($this->isMulti) {
|
1164 |
+
call_user_func_array(array($this->redisMulti, $name), $args);
|
1165 |
+
return $this;
|
1166 |
+
}
|
1167 |
+
|
1168 |
+
// Send request, retry one time when using persistent connections on the first request only
|
1169 |
+
$this->requests++;
|
1170 |
+
try {
|
1171 |
+
$response = call_user_func_array(array($this->redis, $name), $args);
|
1172 |
+
} catch (RedisException $e) {
|
1173 |
+
if ($this->persistent && $this->requests == 1 && $e->getMessage() == 'read error on connection') {
|
1174 |
+
$this->close(true);
|
1175 |
+
$this->connect();
|
1176 |
+
$response = call_user_func_array(array($this->redis, $name), $args);
|
1177 |
+
} else {
|
1178 |
+
throw $e;
|
1179 |
+
}
|
1180 |
+
}
|
1181 |
+
}
|
1182 |
+
// Wrap exceptions
|
1183 |
+
catch(RedisException $e) {
|
1184 |
+
$code = 0;
|
1185 |
+
if ( ! ($result = $this->redis->IsConnected())) {
|
1186 |
+
$this->close(true);
|
1187 |
+
$code = CredisException::CODE_DISCONNECTED;
|
1188 |
+
}
|
1189 |
+
throw new CredisException($e->getMessage(), $code, $e);
|
1190 |
+
}
|
1191 |
+
|
1192 |
+
#echo "> $name : ".substr(print_r($response, TRUE),0,100)."\n";
|
1193 |
+
|
1194 |
+
// change return values where it is too difficult to minim in standalone mode
|
1195 |
+
switch($name)
|
1196 |
+
{
|
1197 |
+
case 'type':
|
1198 |
+
$typeMap = array(
|
1199 |
+
self::TYPE_NONE,
|
1200 |
+
self::TYPE_STRING,
|
1201 |
+
self::TYPE_SET,
|
1202 |
+
self::TYPE_LIST,
|
1203 |
+
self::TYPE_ZSET,
|
1204 |
+
self::TYPE_HASH,
|
1205 |
+
);
|
1206 |
+
$response = $typeMap[$response];
|
1207 |
+
break;
|
1208 |
+
|
1209 |
+
// Handle scripting errors
|
1210 |
+
case 'eval':
|
1211 |
+
case 'evalsha':
|
1212 |
+
case 'script':
|
1213 |
+
$error = $this->redis->getLastError();
|
1214 |
+
$this->redis->clearLastError();
|
1215 |
+
if ($error && substr($error,0,8) == 'NOSCRIPT') {
|
1216 |
+
$response = NULL;
|
1217 |
+
} else if ($error) {
|
1218 |
+
throw new CredisException($error);
|
1219 |
+
}
|
1220 |
+
break;
|
1221 |
+
case 'exists':
|
1222 |
+
// smooth over phpredis-v4 vs earlier difference to match documented credis return results
|
1223 |
+
$response = (int) $response;
|
1224 |
+
break;
|
1225 |
+
case 'ping':
|
1226 |
+
if ($response) {
|
1227 |
+
if ($response === true) {
|
1228 |
+
$response = isset($args[0]) ? $args[0] : "PONG";
|
1229 |
+
} else if ($response[0] === '+') {
|
1230 |
+
$response = substr($response, 1);
|
1231 |
+
}
|
1232 |
+
}
|
1233 |
+
break;
|
1234 |
+
default:
|
1235 |
+
$error = $this->redis->getLastError();
|
1236 |
+
$this->redis->clearLastError();
|
1237 |
+
if ($error) {
|
1238 |
+
throw new CredisException(rtrim($error));
|
1239 |
+
}
|
1240 |
+
break;
|
1241 |
+
}
|
1242 |
+
}
|
1243 |
+
|
1244 |
+
return $response;
|
1245 |
+
}
|
1246 |
+
|
1247 |
+
protected function write_command($command)
|
1248 |
+
{
|
1249 |
+
// Reconnect on lost connection (Redis server "timeout" exceeded since last command)
|
1250 |
+
if(feof($this->redis)) {
|
1251 |
+
// If a watch or transaction was in progress and connection was lost, throw error rather than reconnect
|
1252 |
+
// since transaction/watch state will be lost.
|
1253 |
+
if(($this->isMulti && ! $this->usePipeline) || $this->isWatching) {
|
1254 |
+
$this->close(true);
|
1255 |
+
throw new CredisException('Lost connection to Redis server during watch or transaction.');
|
1256 |
+
}
|
1257 |
+
$this->close(true);
|
1258 |
+
$this->connect();
|
1259 |
+
if($this->authPassword) {
|
1260 |
+
$this->auth($this->authPassword);
|
1261 |
+
}
|
1262 |
+
if($this->selectedDb != 0) {
|
1263 |
+
$this->select($this->selectedDb);
|
1264 |
+
}
|
1265 |
+
}
|
1266 |
+
|
1267 |
+
$commandLen = strlen($command);
|
1268 |
+
$lastFailed = FALSE;
|
1269 |
+
for ($written = 0; $written < $commandLen; $written += $fwrite) {
|
1270 |
+
$fwrite = fwrite($this->redis, substr($command, $written));
|
1271 |
+
if ($fwrite === FALSE || ($fwrite == 0 && $lastFailed)) {
|
1272 |
+
$this->close(true);
|
1273 |
+
throw new CredisException('Failed to write entire command to stream');
|
1274 |
+
}
|
1275 |
+
$lastFailed = $fwrite == 0;
|
1276 |
+
}
|
1277 |
+
}
|
1278 |
+
|
1279 |
+
protected function read_reply($name = '', $returnQueued = false)
|
1280 |
+
{
|
1281 |
+
$reply = fgets($this->redis);
|
1282 |
+
if($reply === FALSE) {
|
1283 |
+
$info = stream_get_meta_data($this->redis);
|
1284 |
+
$this->close(true);
|
1285 |
+
if ($info['timed_out']) {
|
1286 |
+
throw new CredisException('Read operation timed out.', CredisException::CODE_TIMED_OUT);
|
1287 |
+
} else {
|
1288 |
+
throw new CredisException('Lost connection to Redis server.', CredisException::CODE_DISCONNECTED);
|
1289 |
+
}
|
1290 |
+
}
|
1291 |
+
$reply = rtrim($reply, CRLF);
|
1292 |
+
#echo "> $name: $reply\n";
|
1293 |
+
$replyType = substr($reply, 0, 1);
|
1294 |
+
switch ($replyType) {
|
1295 |
+
/* Error reply */
|
1296 |
+
case '-':
|
1297 |
+
if($this->isMulti || $this->usePipeline) {
|
1298 |
+
$response = FALSE;
|
1299 |
+
} else if ($name == 'evalsha' && substr($reply,0,9) == '-NOSCRIPT') {
|
1300 |
+
$response = NULL;
|
1301 |
+
} else {
|
1302 |
+
throw new CredisException(substr($reply,0,4) == '-ERR' ? 'ERR '.substr($reply, 5) : substr($reply,1));
|
1303 |
+
}
|
1304 |
+
break;
|
1305 |
+
/* Inline reply */
|
1306 |
+
case '+':
|
1307 |
+
$response = substr($reply, 1);
|
1308 |
+
if($response == 'OK') {
|
1309 |
+
return TRUE;
|
1310 |
+
}
|
1311 |
+
if($response == 'QUEUED') {
|
1312 |
+
return $returnQueued ? null : true;
|
1313 |
+
}
|
1314 |
+
break;
|
1315 |
+
/* Bulk reply */
|
1316 |
+
case '$':
|
1317 |
+
if ($reply == '$-1') return FALSE;
|
1318 |
+
$size = (int) substr($reply, 1);
|
1319 |
+
$response = stream_get_contents($this->redis, $size + 2);
|
1320 |
+
if( ! $response) {
|
1321 |
+
$this->close(true);
|
1322 |
+
throw new CredisException('Error reading reply.');
|
1323 |
+
}
|
1324 |
+
$response = substr($response, 0, $size);
|
1325 |
+
break;
|
1326 |
+
/* Multi-bulk reply */
|
1327 |
+
case '*':
|
1328 |
+
$count = substr($reply, 1);
|
1329 |
+
if ($count == '-1') return FALSE;
|
1330 |
+
|
1331 |
+
$response = array();
|
1332 |
+
for ($i = 0; $i < $count; $i++) {
|
1333 |
+
$response[] = $this->read_reply();
|
1334 |
+
}
|
1335 |
+
break;
|
1336 |
+
/* Integer reply */
|
1337 |
+
case ':':
|
1338 |
+
$response = intval(substr($reply, 1));
|
1339 |
+
break;
|
1340 |
+
default:
|
1341 |
+
throw new CredisException('Invalid response: '.print_r($reply, TRUE));
|
1342 |
+
break;
|
1343 |
+
}
|
1344 |
+
|
1345 |
+
return $response;
|
1346 |
+
}
|
1347 |
+
|
1348 |
+
protected function decode_reply($name, $response, array &$arguments = array() )
|
1349 |
+
{
|
1350 |
+
// Smooth over differences between phpredis and standalone response
|
1351 |
+
switch ($name)
|
1352 |
+
{
|
1353 |
+
case '': // Minor optimization for multi-bulk replies
|
1354 |
+
break;
|
1355 |
+
case 'config':
|
1356 |
+
case 'hgetall':
|
1357 |
+
$keys = $values = array();
|
1358 |
+
while ($response)
|
1359 |
+
{
|
1360 |
+
$keys[] = array_shift($response);
|
1361 |
+
$values[] = array_shift($response);
|
1362 |
+
}
|
1363 |
+
$response = count($keys) ? array_combine($keys, $values) : array();
|
1364 |
+
break;
|
1365 |
+
case 'info':
|
1366 |
+
$lines = explode(CRLF, trim($response, CRLF));
|
1367 |
+
$response = array();
|
1368 |
+
foreach ($lines as $line)
|
1369 |
+
{
|
1370 |
+
if (!$line || substr($line, 0, 1) == '#')
|
1371 |
+
{
|
1372 |
+
continue;
|
1373 |
+
}
|
1374 |
+
list($key, $value) = explode(':', $line, 2);
|
1375 |
+
$response[$key] = $value;
|
1376 |
+
}
|
1377 |
+
break;
|
1378 |
+
case 'ttl':
|
1379 |
+
if ($response === -1)
|
1380 |
+
{
|
1381 |
+
$response = false;
|
1382 |
+
}
|
1383 |
+
break;
|
1384 |
+
case 'hmget':
|
1385 |
+
if (count($arguments) != count($response))
|
1386 |
+
{
|
1387 |
+
throw new CredisException(
|
1388 |
+
'hmget arguments and response do not match: ' . print_r($arguments, true) . ' ' . print_r(
|
1389 |
+
$response, true
|
1390 |
+
)
|
1391 |
+
);
|
1392 |
+
}
|
1393 |
+
// rehydrate results into key => value form
|
1394 |
+
$response = array_combine($arguments, $response);
|
1395 |
+
break;
|
1396 |
+
|
1397 |
+
case 'scan':
|
1398 |
+
case 'sscan':
|
1399 |
+
$arguments[0] = intval(array_shift($response));
|
1400 |
+
$response = empty($response[0]) ? array() : $response[0];
|
1401 |
+
break;
|
1402 |
+
case 'hscan':
|
1403 |
+
case 'zscan':
|
1404 |
+
$arguments[0] = intval(array_shift($response));
|
1405 |
+
$response = empty($response[0]) ? array() : $response[0];
|
1406 |
+
if (!empty($response) && is_array($response))
|
1407 |
+
{
|
1408 |
+
$count = count($response);
|
1409 |
+
$out = array();
|
1410 |
+
for ($i = 0; $i < $count; $i += 2)
|
1411 |
+
{
|
1412 |
+
$out[$response[$i]] = $response[$i + 1];
|
1413 |
+
}
|
1414 |
+
$response = $out;
|
1415 |
+
}
|
1416 |
+
break;
|
1417 |
+
case 'zrangebyscore':
|
1418 |
+
case 'zrevrangebyscore':
|
1419 |
+
case 'zrange':
|
1420 |
+
case 'zrevrange':
|
1421 |
+
if (in_array('withscores', $arguments, true))
|
1422 |
+
{
|
1423 |
+
// Map array of values into key=>score list like phpRedis does
|
1424 |
+
$item = null;
|
1425 |
+
$out = array();
|
1426 |
+
foreach ($response as $value)
|
1427 |
+
{
|
1428 |
+
if ($item == null)
|
1429 |
+
{
|
1430 |
+
$item = $value;
|
1431 |
+
}
|
1432 |
+
else
|
1433 |
+
{
|
1434 |
+
// 2nd value is the score
|
1435 |
+
$out[$item] = (float)$value;
|
1436 |
+
$item = null;
|
1437 |
+
}
|
1438 |
+
}
|
1439 |
+
$response = $out;
|
1440 |
+
}
|
1441 |
+
break;
|
1442 |
+
}
|
1443 |
+
|
1444 |
+
return $response;
|
1445 |
+
}
|
1446 |
+
|
1447 |
+
/**
|
1448 |
+
* Build the Redis unified protocol command
|
1449 |
+
*
|
1450 |
+
* @param array $args
|
1451 |
+
* @return string
|
1452 |
+
*/
|
1453 |
+
private static function _prepare_command($args)
|
1454 |
+
{
|
1455 |
+
return sprintf('*%d%s%s%s', count($args), CRLF, implode(CRLF, array_map(array('self', '_map'), $args)), CRLF);
|
1456 |
+
}
|
1457 |
+
|
1458 |
+
private static function _map($arg)
|
1459 |
+
{
|
1460 |
+
return sprintf('$%d%s%s', strlen($arg), CRLF, $arg);
|
1461 |
+
}
|
1462 |
+
|
1463 |
+
/**
|
1464 |
+
* Flatten arguments
|
1465 |
+
*
|
1466 |
+
* If an argument is an array, the key is inserted as argument followed by the array values
|
1467 |
+
* array('zrangebyscore', '-inf', 123, array('limit' => array('0', '1')))
|
1468 |
+
* becomes
|
1469 |
+
* array('zrangebyscore', '-inf', 123, 'limit', '0', '1')
|
1470 |
+
*
|
1471 |
+
* @param array $in
|
1472 |
+
* @return array
|
1473 |
+
*/
|
1474 |
+
private static function _flattenArguments(array $arguments, &$out = array())
|
1475 |
+
{
|
1476 |
+
foreach ($arguments as $key => $arg) {
|
1477 |
+
if (!is_int($key)) {
|
1478 |
+
$out[] = $key;
|
1479 |
+
}
|
1480 |
+
|
1481 |
+
if (is_array($arg)) {
|
1482 |
+
self::_flattenArguments($arg, $out);
|
1483 |
+
} else {
|
1484 |
+
$out[] = $arg;
|
1485 |
+
}
|
1486 |
+
}
|
1487 |
+
|
1488 |
+
return $out;
|
1489 |
+
}
|
1490 |
+
}
|
dependencies/vendor/colinmollenhour/credis/Cluster.php
ADDED
@@ -0,0 +1,343 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Credis, a Redis interface for the modest
|
4 |
+
*
|
5 |
+
* @author Justin Poliey <jdp34@njit.edu>
|
6 |
+
* @copyright 2009 Justin Poliey <jdp34@njit.edu>
|
7 |
+
* @license http://www.opensource.org/licenses/mit-license.php The MIT License
|
8 |
+
* @package Credis
|
9 |
+
*/
|
10 |
+
|
11 |
+
/**
|
12 |
+
* A generalized Credis_Client interface for a cluster of Redis servers
|
13 |
+
*
|
14 |
+
* @deprecated
|
15 |
+
*/
|
16 |
+
class Credis_Cluster
|
17 |
+
{
|
18 |
+
/**
|
19 |
+
* Collection of Credis_Client objects attached to Redis servers
|
20 |
+
* @var Credis_Client[]
|
21 |
+
*/
|
22 |
+
protected $clients;
|
23 |
+
/**
|
24 |
+
* If a server is set as master, all write commands go to that one
|
25 |
+
* @var Credis_Client
|
26 |
+
*/
|
27 |
+
protected $masterClient;
|
28 |
+
/**
|
29 |
+
* Aliases of Credis_Client objects attached to Redis servers, used to route commands to specific servers
|
30 |
+
* @see Credis_Cluster::to
|
31 |
+
* @var array
|
32 |
+
*/
|
33 |
+
protected $aliases;
|
34 |
+
|
35 |
+
/**
|
36 |
+
* Hash ring of Redis server nodes
|
37 |
+
* @var array
|
38 |
+
*/
|
39 |
+
protected $ring;
|
40 |
+
|
41 |
+
/**
|
42 |
+
* Individual nodes of pointers to Redis servers on the hash ring
|
43 |
+
* @var array
|
44 |
+
*/
|
45 |
+
protected $nodes;
|
46 |
+
|
47 |
+
/**
|
48 |
+
* The commands that are not subject to hashing
|
49 |
+
* @var array
|
50 |
+
* @access protected
|
51 |
+
*/
|
52 |
+
protected $dont_hash;
|
53 |
+
|
54 |
+
/**
|
55 |
+
* Currently working cluster-wide database number.
|
56 |
+
* @var int
|
57 |
+
*/
|
58 |
+
protected $selectedDb = 0;
|
59 |
+
|
60 |
+
/**
|
61 |
+
* Creates an interface to a cluster of Redis servers
|
62 |
+
* Each server should be in the format:
|
63 |
+
* array(
|
64 |
+
* 'host' => hostname,
|
65 |
+
* 'port' => port,
|
66 |
+
* 'db' => db,
|
67 |
+
* 'password' => password,
|
68 |
+
* 'timeout' => timeout,
|
69 |
+
* 'alias' => alias,
|
70 |
+
* 'persistent' => persistence_identifier,
|
71 |
+
* 'master' => master
|
72 |
+
* 'write_only'=> true/false
|
73 |
+
* )
|
74 |
+
*
|
75 |
+
* @param array $servers The Redis servers in the cluster.
|
76 |
+
* @param int $replicas
|
77 |
+
* @param bool $standAlone
|
78 |
+
* @throws CredisException
|
79 |
+
*/
|
80 |
+
public function __construct($servers, $replicas = 128, $standAlone = false)
|
81 |
+
{
|
82 |
+
$this->clients = array();
|
83 |
+
$this->masterClient = null;
|
84 |
+
$this->aliases = array();
|
85 |
+
$this->ring = array();
|
86 |
+
$this->replicas = (int)$replicas;
|
87 |
+
$client = null;
|
88 |
+
foreach ($servers as $server)
|
89 |
+
{
|
90 |
+
if(is_array($server)){
|
91 |
+
$client = new Credis_Client(
|
92 |
+
$server['host'],
|
93 |
+
$server['port'],
|
94 |
+
isset($server['timeout']) ? $server['timeout'] : 2.5,
|
95 |
+
isset($server['persistent']) ? $server['persistent'] : '',
|
96 |
+
isset($server['db']) ? $server['db'] : 0,
|
97 |
+
isset($server['password']) ? $server['password'] : null
|
98 |
+
);
|
99 |
+
if (isset($server['alias'])) {
|
100 |
+
$this->aliases[$server['alias']] = $client;
|
101 |
+
}
|
102 |
+
if(isset($server['master']) && $server['master'] === true){
|
103 |
+
$this->masterClient = $client;
|
104 |
+
if(isset($server['write_only']) && $server['write_only'] === true){
|
105 |
+
continue;
|
106 |
+
}
|
107 |
+
}
|
108 |
+
} elseif($server instanceof Credis_Client){
|
109 |
+
$client = $server;
|
110 |
+
} else {
|
111 |
+
throw new CredisException('Server should either be an array or an instance of Credis_Client');
|
112 |
+
}
|
113 |
+
if($standAlone) {
|
114 |
+
$client->forceStandalone();
|
115 |
+
}
|
116 |
+
$this->clients[] = $client;
|
117 |
+
for ($replica = 0; $replica <= $this->replicas; $replica++) {
|
118 |
+
$md5num = hexdec(substr(md5($client->getHost().':'.$client->getPort().'-'.$replica),0,7));
|
119 |
+
$this->ring[$md5num] = count($this->clients)-1;
|
120 |
+
}
|
121 |
+
}
|
122 |
+
ksort($this->ring, SORT_NUMERIC);
|
123 |
+
$this->nodes = array_keys($this->ring);
|
124 |
+
$this->dont_hash = array_flip(array(
|
125 |
+
'RANDOMKEY', 'DBSIZE', 'PIPELINE', 'EXEC',
|
126 |
+
'SELECT', 'MOVE', 'FLUSHDB', 'FLUSHALL',
|
127 |
+
'SAVE', 'BGSAVE', 'LASTSAVE', 'SHUTDOWN',
|
128 |
+
'INFO', 'MONITOR', 'SLAVEOF'
|
129 |
+
));
|
130 |
+
if($this->masterClient !== null && count($this->clients()) == 0){
|
131 |
+
$this->clients[] = $this->masterClient;
|
132 |
+
for ($replica = 0; $replica <= $this->replicas; $replica++) {
|
133 |
+
$md5num = hexdec(substr(md5($this->masterClient->getHost().':'.$this->masterClient->getHost().'-'.$replica),0,7));
|
134 |
+
$this->ring[$md5num] = count($this->clients)-1;
|
135 |
+
}
|
136 |
+
$this->nodes = array_keys($this->ring);
|
137 |
+
}
|
138 |
+
}
|
139 |
+
|
140 |
+
/**
|
141 |
+
* @param Credis_Client $masterClient
|
142 |
+
* @param bool $writeOnly
|
143 |
+
* @return Credis_Cluster
|
144 |
+
*/
|
145 |
+
public function setMasterClient(Credis_Client $masterClient, $writeOnly=false)
|
146 |
+
{
|
147 |
+
if(!$masterClient instanceof Credis_Client){
|
148 |
+
throw new CredisException('Master client should be an instance of Credis_Client');
|
149 |
+
}
|
150 |
+
$this->masterClient = $masterClient;
|
151 |
+
if (!isset($this->aliases['master'])) {
|
152 |
+
$this->aliases['master'] = $masterClient;
|
153 |
+
}
|
154 |
+
if(!$writeOnly){
|
155 |
+
$this->clients[] = $this->masterClient;
|
156 |
+
for ($replica = 0; $replica <= $this->replicas; $replica++) {
|
157 |
+
$md5num = hexdec(substr(md5($this->masterClient->getHost().':'.$this->masterClient->getHost().'-'.$replica),0,7));
|
158 |
+
$this->ring[$md5num] = count($this->clients)-1;
|
159 |
+
}
|
160 |
+
$this->nodes = array_keys($this->ring);
|
161 |
+
}
|
162 |
+
return $this;
|
163 |
+
}
|
164 |
+
/**
|
165 |
+
* Get a client by index or alias.
|
166 |
+
*
|
167 |
+
* @param string|int $alias
|
168 |
+
* @throws CredisException
|
169 |
+
* @return Credis_Client
|
170 |
+
*/
|
171 |
+
public function client($alias)
|
172 |
+
{
|
173 |
+
if (is_int($alias) && isset($this->clients[$alias])) {
|
174 |
+
return $this->clients[$alias];
|
175 |
+
}
|
176 |
+
else if (isset($this->aliases[$alias])) {
|
177 |
+
return $this->aliases[$alias];
|
178 |
+
}
|
179 |
+
throw new CredisException("Client $alias does not exist.");
|
180 |
+
}
|
181 |
+
|
182 |
+
/**
|
183 |
+
* Get an array of all clients
|
184 |
+
*
|
185 |
+
* @return array|Credis_Client[]
|
186 |
+
*/
|
187 |
+
public function clients()
|
188 |
+
{
|
189 |
+
return $this->clients;
|
190 |
+
}
|
191 |
+
|
192 |
+
/**
|
193 |
+
* Execute a command on all clients
|
194 |
+
*
|
195 |
+
* @return array
|
196 |
+
*/
|
197 |
+
public function all()
|
198 |
+
{
|
199 |
+
$args = func_get_args();
|
200 |
+
$name = array_shift($args);
|
201 |
+
$results = array();
|
202 |
+
foreach($this->clients as $client) {
|
203 |
+
$results[] = call_user_func_array([$client, $name], $args);
|
204 |
+
}
|
205 |
+
return $results;
|
206 |
+
}
|
207 |
+
|
208 |
+
/**
|
209 |
+
* Get the client that the key would hash to.
|
210 |
+
*
|
211 |
+
* @param string $key
|
212 |
+
* @return \Credis_Client
|
213 |
+
*/
|
214 |
+
public function byHash($key)
|
215 |
+
{
|
216 |
+
return $this->clients[$this->hash($key)];
|
217 |
+
}
|
218 |
+
|
219 |
+
/**
|
220 |
+
* @param int $index
|
221 |
+
* @return void
|
222 |
+
*/
|
223 |
+
public function select($index)
|
224 |
+
{
|
225 |
+
$this->selectedDb = (int) $index;
|
226 |
+
}
|
227 |
+
|
228 |
+
/**
|
229 |
+
* Execute a Redis command on the cluster with automatic consistent hashing and read/write splitting
|
230 |
+
*
|
231 |
+
* @param string $name
|
232 |
+
* @param array $args
|
233 |
+
* @return mixed
|
234 |
+
*/
|
235 |
+
public function __call($name, $args)
|
236 |
+
{
|
237 |
+
if($this->masterClient !== null && !$this->isReadOnlyCommand($name)){
|
238 |
+
$client = $this->masterClient;
|
239 |
+
}elseif (count($this->clients()) == 1 || isset($this->dont_hash[strtoupper($name)]) || !isset($args[0])) {
|
240 |
+
$client = $this->clients[0];
|
241 |
+
}
|
242 |
+
else {
|
243 |
+
$hashKey = $args[0];
|
244 |
+
if (is_array($hashKey)) {
|
245 |
+
$hashKey = join('|', $hashKey);
|
246 |
+
}
|
247 |
+
$client = $this->byHash($hashKey);
|
248 |
+
}
|
249 |
+
// Ensure that current client is working on the same database as expected.
|
250 |
+
if ($client->getSelectedDb() != $this->selectedDb) {
|
251 |
+
$client->select($this->selectedDb);
|
252 |
+
}
|
253 |
+
return call_user_func_array([$client, $name], $args);
|
254 |
+
}
|
255 |
+
|
256 |
+
/**
|
257 |
+
* Get client index for a key by searching ring with binary search
|
258 |
+
*
|
259 |
+
* @param string $key The key to hash
|
260 |
+
* @return int The index of the client object associated with the hash of the key
|
261 |
+
*/
|
262 |
+
public function hash($key)
|
263 |
+
{
|
264 |
+
$needle = hexdec(substr(md5($key),0,7));
|
265 |
+
$server = $min = 0;
|
266 |
+
$max = count($this->nodes) - 1;
|
267 |
+
while ($max >= $min) {
|
268 |
+
$position = (int) (($min + $max) / 2);
|
269 |
+
$server = $this->nodes[$position];
|
270 |
+
if ($needle < $server) {
|
271 |
+
$max = $position - 1;
|
272 |
+
}
|
273 |
+
else if ($needle > $server) {
|
274 |
+
$min = $position + 1;
|
275 |
+
}
|
276 |
+
else {
|
277 |
+
break;
|
278 |
+
}
|
279 |
+
}
|
280 |
+
return $this->ring[$server];
|
281 |
+
}
|
282 |
+
|
283 |
+
public function isReadOnlyCommand($command)
|
284 |
+
{
|
285 |
+
static $readOnlyCommands = array(
|
286 |
+
'DBSIZE' => true,
|
287 |
+
'INFO' => true,
|
288 |
+
'MONITOR' => true,
|
289 |
+
'EXISTS' => true,
|
290 |
+
'TYPE' => true,
|
291 |
+
'KEYS' => true,
|
292 |
+
'SCAN' => true,
|
293 |
+
'RANDOMKEY' => true,
|
294 |
+
'TTL' => true,
|
295 |
+
'GET' => true,
|
296 |
+
'MGET' => true,
|
297 |
+
'SUBSTR' => true,
|
298 |
+
'STRLEN' => true,
|
299 |
+
'GETRANGE' => true,
|
300 |
+
'GETBIT' => true,
|
301 |
+
'LLEN' => true,
|
302 |
+
'LRANGE' => true,
|
303 |
+
'LINDEX' => true,
|
304 |
+
'SCARD' => true,
|
305 |
+
'SISMEMBER' => true,
|
306 |
+
'SINTER' => true,
|
307 |
+
'SUNION' => true,
|
308 |
+
'SDIFF' => true,
|
309 |
+
'SMEMBERS' => true,
|
310 |
+
'SSCAN' => true,
|
311 |
+
'SRANDMEMBER' => true,
|
312 |
+
'ZRANGE' => true,
|
313 |
+
'ZREVRANGE' => true,
|
314 |
+
'ZRANGEBYSCORE' => true,
|
315 |
+
'ZREVRANGEBYSCORE' => true,
|
316 |
+
'ZCARD' => true,
|
317 |
+
'ZSCORE' => true,
|
318 |
+
'ZCOUNT' => true,
|
319 |
+
'ZRANK' => true,
|
320 |
+
'ZREVRANK' => true,
|
321 |
+
'ZSCAN' => true,
|
322 |
+
'HGET' => true,
|
323 |
+
'HMGET' => true,
|
324 |
+
'HEXISTS' => true,
|
325 |
+
'HLEN' => true,
|
326 |
+
'HKEYS' => true,
|
327 |
+
'HVALS' => true,
|
328 |
+
'HGETALL' => true,
|
329 |
+
'HSCAN' => true,
|
330 |
+
'PING' => true,
|
331 |
+
'AUTH' => true,
|
332 |
+
'SELECT' => true,
|
333 |
+
'ECHO' => true,
|
334 |
+
'QUIT' => true,
|
335 |
+
'OBJECT' => true,
|
336 |
+
'BITCOUNT' => true,
|
337 |
+
'TIME' => true,
|
338 |
+
'SORT' => true,
|
339 |
+
);
|
340 |
+
return array_key_exists(strtoupper($command), $readOnlyCommands);
|
341 |
+
}
|
342 |
+
}
|
343 |
+
|
dependencies/vendor/colinmollenhour/credis/LICENSE
ADDED
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
Copyright (c) 2009 Justin Poliey <jdp34@njit.edu>
|
2 |
+
Copyright (c) 2011 Colin Mollenhour <colin@mollenhour.com>
|
3 |
+
|
4 |
+
Permission is hereby granted, free of charge, to any person
|
5 |
+
obtaining a copy of this software and associated documentation
|
6 |
+
files (the "Software"), to deal in the Software without
|
7 |
+
restriction, including without limitation the rights to use,
|
8 |
+
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9 |
+
copies of the Software, and to permit persons to whom the
|
10 |
+
Software is furnished to do so, subject to the following
|
11 |
+
conditions:
|
12 |
+
|
13 |
+
The above copyright notice and this permission notice shall be
|
14 |
+
included in all copies or substantial portions of the Software.
|
15 |
+
|
16 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17 |
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
18 |
+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19 |
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
20 |
+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
21 |
+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
22 |
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
23 |
+
OTHER DEALINGS IN THE SOFTWARE.
|
dependencies/vendor/colinmollenhour/credis/Module.php
ADDED
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Credis_Module
|
5 |
+
*
|
6 |
+
* Implements Redis Modules support. see http://redismodules.com
|
7 |
+
*
|
8 |
+
* @author Igor Veremchuk <igor.veremchuk@gmail.com>
|
9 |
+
* @license http://www.opensource.org/licenses/mit-license.php The MIT License
|
10 |
+
* @package Credis_Module
|
11 |
+
*/
|
12 |
+
class Credis_Module
|
13 |
+
{
|
14 |
+
const MODULE_COUNTING_BLOOM_FILTER = 'CBF';
|
15 |
+
|
16 |
+
/** @var Credis_Client */
|
17 |
+
protected $client;
|
18 |
+
|
19 |
+
/** @var string */
|
20 |
+
protected $moduleName;
|
21 |
+
|
22 |
+
/**
|
23 |
+
* @param Credis_Client $client
|
24 |
+
* @param string $module
|
25 |
+
*/
|
26 |
+
public function __construct(Credis_Client $client, $module = null)
|
27 |
+
{
|
28 |
+
$client->forceStandalone(); // Redis Modules command not currently supported by phpredis
|
29 |
+
$this->client = $client;
|
30 |
+
|
31 |
+
if (isset($module)) {
|
32 |
+
$this->setModule($module);
|
33 |
+
}
|
34 |
+
}
|
35 |
+
|
36 |
+
/**
|
37 |
+
* Clean up client on destruct
|
38 |
+
*/
|
39 |
+
public function __destruct()
|
40 |
+
{
|
41 |
+
$this->client->close();
|
42 |
+
}
|
43 |
+
|
44 |
+
/**
|
45 |
+
* @param $moduleName
|
46 |
+
* @return $this
|
47 |
+
*/
|
48 |
+
public function setModule($moduleName)
|
49 |
+
{
|
50 |
+
$this->moduleName = (string) $moduleName;
|
51 |
+
|
52 |
+
return $this;
|
53 |
+
}
|
54 |
+
|
55 |
+
/**
|
56 |
+
* @param string $name
|
57 |
+
* @param string $args
|
58 |
+
* @return mixed
|
59 |
+
*/
|
60 |
+
public function __call($name, $args)
|
61 |
+
{
|
62 |
+
if ($this->moduleName === null) {
|
63 |
+
throw new \LogicException('Module must be set.');
|
64 |
+
}
|
65 |
+
|
66 |
+
return call_user_func(array($this->client, sprintf('%s.%s', $this->moduleName, $name)), $args);
|
67 |
+
}
|
68 |
+
}
|
dependencies/vendor/colinmollenhour/credis/README.markdown
ADDED
@@ -0,0 +1,206 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
[![Build Status](https://travis-ci.org/colinmollenhour/credis.svg?branch=master)](https://travis-ci.org/colinmollenhour/credis)
|
2 |
+
|
3 |
+
# Credis
|
4 |
+
|
5 |
+
Credis is a lightweight interface to the [Redis](http://redis.io/) key-value store which wraps the [phpredis](https://github.com/nicolasff/phpredis)
|
6 |
+
library when available for better performance. This project was forked from one of the many redisent forks.
|
7 |
+
|
8 |
+
## Getting Started
|
9 |
+
|
10 |
+
Credis_Client uses methods named the same as Redis commands, and translates return values to the appropriate
|
11 |
+
PHP equivalents.
|
12 |
+
|
13 |
+
```php
|
14 |
+
require 'Credis/Client.php';
|
15 |
+
$redis = new Credis_Client('localhost');
|
16 |
+
$redis->set('awesome', 'absolutely');
|
17 |
+
echo sprintf('Is Credis awesome? %s.\n', $redis->get('awesome'));
|
18 |
+
|
19 |
+
// When arrays are given as arguments they are flattened automatically
|
20 |
+
$redis->rpush('particles', array('proton','electron','neutron'));
|
21 |
+
$particles = $redis->lrange('particles', 0, -1);
|
22 |
+
```
|
23 |
+
Redis error responses will be wrapped in a CredisException class and thrown.
|
24 |
+
|
25 |
+
Credis_Client also supports transparent command renaming. Write code using the original command names and the
|
26 |
+
client will send the aliased commands to the server transparently. Specify the renamed commands using a prefix
|
27 |
+
for md5, a callable function, individual aliases, or an array map of aliases. See "Redis Security":http://redis.io/topics/security for more info.
|
28 |
+
|
29 |
+
## Supported connection string formats
|
30 |
+
|
31 |
+
```php
|
32 |
+
$redis = new Credis_Client(/* connection string */);
|
33 |
+
```
|
34 |
+
|
35 |
+
### Unix socket connection string
|
36 |
+
|
37 |
+
`unix:///path/to/redis.sock`
|
38 |
+
|
39 |
+
### TCP connection string
|
40 |
+
|
41 |
+
`tcp://host[:port][/persistence_identifier]`
|
42 |
+
|
43 |
+
### TLS connection string
|
44 |
+
|
45 |
+
`tls://host[:port][/persistence_identifier]`
|
46 |
+
|
47 |
+
#### Enable transport level security (TLS)
|
48 |
+
|
49 |
+
Use TLS connection string `tls://127.0.0.1:6379` instead of TCP connection `tcp://127.0.0.1:6379` string in order to enable transport level security.
|
50 |
+
|
51 |
+
```php
|
52 |
+
require 'Credis/Client.php';
|
53 |
+
$redis = new Credis_Client('tls://127.0.0.1:6379');
|
54 |
+
$redis->set('awesome', 'absolutely');
|
55 |
+
echo sprintf('Is Credis awesome? %s.\n', $redis->get('awesome'));
|
56 |
+
|
57 |
+
// When arrays are given as arguments they are flattened automatically
|
58 |
+
$redis->rpush('particles', array('proton','electron','neutron'));
|
59 |
+
$particles = $redis->lrange('particles', 0, -1);
|
60 |
+
```
|
61 |
+
|
62 |
+
## Clustering your servers
|
63 |
+
|
64 |
+
Credis also includes a way for developers to fully utilize the scalability of Redis with multiple servers and [consistent hashing](http://en.wikipedia.org/wiki/Consistent_hashing).
|
65 |
+
Using the [Credis_Cluster](Cluster.php) class, you can use Credis the same way, except that keys will be hashed across multiple servers.
|
66 |
+
Here is how to set up a cluster:
|
67 |
+
|
68 |
+
### Basic clustering example
|
69 |
+
```php
|
70 |
+
<?php
|
71 |
+
require 'Credis/Client.php';
|
72 |
+
require 'Credis/Cluster.php';
|
73 |
+
|
74 |
+
$cluster = new Credis_Cluster(array(
|
75 |
+
array('host' => '127.0.0.1', 'port' => 6379, 'alias'=>'alpha'),
|
76 |
+
array('host' => '127.0.0.1', 'port' => 6380, 'alias'=>'beta')
|
77 |
+
));
|
78 |
+
$cluster->set('key','value');
|
79 |
+
echo "Alpha: ".$cluster->client('alpha')->get('key').PHP_EOL;
|
80 |
+
echo "Beta: ".$cluster->client('beta')->get('key').PHP_EOL;
|
81 |
+
```
|
82 |
+
|
83 |
+
### Explicit definition of replicas
|
84 |
+
|
85 |
+
The consistent hashing strategy stores keys on a so called "ring". The position of each key is relative to the position of its target node. The target node that has the closest position will be the selected node for that specific key.
|
86 |
+
|
87 |
+
To avoid an uneven distribution of keys (especially on small clusters), it is common to duplicate target nodes. Based on the number of replicas, each target node will exist *n times* on the "ring".
|
88 |
+
|
89 |
+
The following example explicitly sets the number of replicas to 5. Both Redis instances will have 5 copies. The default value is 128.
|
90 |
+
|
91 |
+
```php
|
92 |
+
<?php
|
93 |
+
require 'Credis/Client.php';
|
94 |
+
require 'Credis/Cluster.php';
|
95 |
+
|
96 |
+
$cluster = new Credis_Cluster(
|
97 |
+
array(
|
98 |
+
array('host' => '127.0.0.1', 'port' => 6379, 'alias'=>'alpha'),
|
99 |
+
array('host' => '127.0.0.1', 'port' => 6380, 'alias'=>'beta')
|
100 |
+
), 5
|
101 |
+
);
|
102 |
+
$cluster->set('key','value');
|
103 |
+
echo "Alpha: ".$cluster->client('alpha')->get('key').PHP_EOL;
|
104 |
+
echo "Beta: ".$cluster->client('beta')->get('key').PHP_EOL;
|
105 |
+
```
|
106 |
+
|
107 |
+
## Master/slave replication
|
108 |
+
|
109 |
+
The [Credis_Cluster](Cluster.php) class can also be used for [master/slave replication](http://redis.io/topics/replication).
|
110 |
+
Credis_Cluster will automatically perform *read/write splitting* and send the write requests exclusively to the master server.
|
111 |
+
Read requests will be handled by all servers unless you set the *write_only* flag to true in the connection string of the master server.
|
112 |
+
|
113 |
+
### Redis server settings for master/slave replication
|
114 |
+
|
115 |
+
Setting up master/slave replication is simple and only requires adding the following line to the config of the slave server:
|
116 |
+
|
117 |
+
```
|
118 |
+
slaveof 127.0.0.1 6379
|
119 |
+
```
|
120 |
+
|
121 |
+
### Basic master/slave example
|
122 |
+
```php
|
123 |
+
<?php
|
124 |
+
require 'Credis/Client.php';
|
125 |
+
require 'Credis/Cluster.php';
|
126 |
+
|
127 |
+
$cluster = new Credis_Cluster(array(
|
128 |
+
array('host' => '127.0.0.1', 'port' => 6379, 'alias'=>'master', 'master'=>true),
|
129 |
+
array('host' => '127.0.0.1', 'port' => 6380, 'alias'=>'slave')
|
130 |
+
));
|
131 |
+
$cluster->set('key','value');
|
132 |
+
echo $cluster->get('key').PHP_EOL;
|
133 |
+
echo $cluster->client('slave')->get('key').PHP_EOL;
|
134 |
+
|
135 |
+
$cluster->client('master')->set('key2','value');
|
136 |
+
echo $cluster->client('slave')->get('key2').PHP_EOL;
|
137 |
+
```
|
138 |
+
|
139 |
+
### No read on master
|
140 |
+
|
141 |
+
The following example illustrates how to disable reading on the master server. This will cause the master server only to be used for writing.
|
142 |
+
This should only happen when you have enough write calls to create a certain load on the master server. Otherwise this is an inefficient usage of server resources.
|
143 |
+
|
144 |
+
```php
|
145 |
+
<?php
|
146 |
+
require 'Credis/Client.php';
|
147 |
+
require 'Credis/Cluster.php';
|
148 |
+
|
149 |
+
$cluster = new Credis_Cluster(array(
|
150 |
+
array('host' => '127.0.0.1', 'port' => 6379, 'alias'=>'master', 'master'=>true, 'write_only'=>true),
|
151 |
+
array('host' => '127.0.0.1', 'port' => 6380, 'alias'=>'slave')
|
152 |
+
));
|
153 |
+
$cluster->set('key','value');
|
154 |
+
echo $cluster->get('key').PHP_EOL;
|
155 |
+
```
|
156 |
+
## Automatic failover with Sentinel
|
157 |
+
|
158 |
+
[Redis Sentinel](http://redis.io/topics/sentinel) is a system that can monitor Redis instances. You register master servers and Sentinel automatically detects its slaves.
|
159 |
+
|
160 |
+
When a master server dies, Sentinel will make sure one of the slaves is promoted to be the new master. This autofailover mechanism will also demote failed masters to avoid data inconsistency.
|
161 |
+
|
162 |
+
The [Credis_Sentinel](Sentinel.php) class interacts with the *Redis Sentinel* instance(s) and acts as a proxy. Sentinel will automatically create [Credis_Cluster](Cluster.php) objects and will set the master and slaves accordingly.
|
163 |
+
|
164 |
+
Sentinel uses the same protocol as Redis. In the example below we register the Sentinel server running on port *26379* and assign it to the [Credis_Sentinel](Sentinel.php) object.
|
165 |
+
We then ask Sentinel the hostname and port for the master server known as *mymaster*. By calling the *getCluster* method we immediately get a [Credis_Cluster](Cluster.php) object that allows us to perform basic Redis calls.
|
166 |
+
|
167 |
+
```php
|
168 |
+
<?php
|
169 |
+
require 'Credis/Client.php';
|
170 |
+
require 'Credis/Cluster.php';
|
171 |
+
require 'Credis/Sentinel.php';
|
172 |
+
|
173 |
+
$sentinel = new Credis_Sentinel(new Credis_Client('127.0.0.1',26379));
|
174 |
+
$masterAddress = $sentinel->getMasterAddressByName('mymaster');
|
175 |
+
$cluster = $sentinel->getCluster('mymaster');
|
176 |
+
|
177 |
+
echo 'Writing to master: '.$masterAddress[0].' on port '.$masterAddress[1].PHP_EOL;
|
178 |
+
$cluster->set('key','value');
|
179 |
+
echo $cluster->get('key').PHP_EOL;
|
180 |
+
```
|
181 |
+
### Additional parameters
|
182 |
+
|
183 |
+
Because [Credis_Sentinel](Sentinel.php) will create [Credis_Cluster](Cluster.php) objects using the *"getCluster"* or *"createCluster"* methods, additional parameters can be passed.
|
184 |
+
|
185 |
+
First of all there's the *"write_only"* flag. You can also define the selected database and the number of replicas. And finally there's a *"selectRandomSlave"* option.
|
186 |
+
|
187 |
+
The *"selectRandomSlave"* flag is used in setups for masters that have multiple slaves. The Credis_Sentinel will either select one random slave to be used when creating the Credis_Cluster object or to pass them all and use the built-in hashing.
|
188 |
+
|
189 |
+
The example below shows how to use these 3 options. It selects database 2, sets the number of replicas to 10, it doesn't select a random slave and doesn't allow reading on the master server.
|
190 |
+
|
191 |
+
```php
|
192 |
+
<?php
|
193 |
+
require 'Credis/Client.php';
|
194 |
+
require 'Credis/Cluster.php';
|
195 |
+
require 'Credis/Sentinel.php';
|
196 |
+
|
197 |
+
$sentinel = new Credis_Sentinel(new Credis_Client('127.0.0.1',26379));
|
198 |
+
$cluster = $sentinel->getCluster('mymaster',2,10,false,true);
|
199 |
+
$cluster->set('key','value');
|
200 |
+
echo $cluster->get('key').PHP_EOL;
|
201 |
+
```
|
202 |
+
|
203 |
+
## About
|
204 |
+
|
205 |
+
© 2011 [Colin Mollenhour](http://colin.mollenhour.com)
|
206 |
+
© 2009 [Justin Poliey](http://justinpoliey.com)
|
dependencies/vendor/colinmollenhour/credis/Sentinel.php
ADDED
@@ -0,0 +1,360 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Credis_Sentinel
|
4 |
+
*
|
5 |
+
* Implements the Sentinel API as mentioned on http://redis.io/topics/sentinel.
|
6 |
+
* Sentinel is aware of master and slave nodes in a cluster and returns instances of Credis_Client accordingly.
|
7 |
+
*
|
8 |
+
* The complexity of read/write splitting can also be abstract by calling the createCluster() method which returns a
|
9 |
+
* Credis_Cluster object that contains both the master server and a random slave. Credis_Cluster takes care of the
|
10 |
+
* read/write splitting
|
11 |
+
*
|
12 |
+
* @author Thijs Feryn <thijs@feryn.eu>
|
13 |
+
* @license http://www.opensource.org/licenses/mit-license.php The MIT License
|
14 |
+
* @package Credis_Sentinel
|
15 |
+
*/
|
16 |
+
class Credis_Sentinel
|
17 |
+
{
|
18 |
+
/**
|
19 |
+
* Contains a client that connects to a Sentinel node.
|
20 |
+
* Sentinel uses the same protocol as Redis which makes using Credis_Client convenient.
|
21 |
+
* @var Credis_Client
|
22 |
+
*/
|
23 |
+
protected $_client;
|
24 |
+
|
25 |
+
/**
|
26 |
+
* Contains an active instance of Credis_Cluster per master pool
|
27 |
+
* @var array
|
28 |
+
*/
|
29 |
+
protected $_cluster = array();
|
30 |
+
|
31 |
+
/**
|
32 |
+
* Contains an active instance of Credis_Client representing a master
|
33 |
+
* @var array
|
34 |
+
*/
|
35 |
+
protected $_master = array();
|
36 |
+
|
37 |
+
/**
|
38 |
+
* Contains an array Credis_Client objects representing all slaves per master pool
|
39 |
+
* @var array
|
40 |
+
*/
|
41 |
+
protected $_slaves = array();
|
42 |
+
|
43 |
+
/**
|
44 |
+
* Use the phpredis extension or the standalone implementation
|
45 |
+
* @var bool
|
46 |
+
* @deprecated
|
47 |
+
*/
|
48 |
+
protected $_standAlone = false;
|
49 |
+
|
50 |
+
/**
|
51 |
+
* Store the AUTH password used by Credis_Client instances
|
52 |
+
* @var string
|
53 |
+
*/
|
54 |
+
protected $_password = '';
|
55 |
+
|
56 |
+
/**
|
57 |
+
* Connect with a Sentinel node. Sentinel will do the master and slave discovery
|
58 |
+
*
|
59 |
+
* @param Credis_Client $client
|
60 |
+
* @param string $password (deprecated - use setClientPassword)
|
61 |
+
* @throws CredisException
|
62 |
+
*/
|
63 |
+
public function __construct(Credis_Client $client, $password = NULL)
|
64 |
+
{
|
65 |
+
if(!$client instanceof Credis_Client){
|
66 |
+
throw new CredisException('Sentinel client should be an instance of Credis_Client');
|
67 |
+
}
|
68 |
+
$client->forceStandalone(); // SENTINEL command not currently supported by phpredis
|
69 |
+
$this->_client = $client;
|
70 |
+
$this->_password = $password;
|
71 |
+
$this->_timeout = NULL;
|
72 |
+
$this->_persistent = '';
|
73 |
+
$this->_db = 0;
|
74 |
+
}
|
75 |
+
|
76 |
+
/**
|
77 |
+
* Clean up client on destruct
|
78 |
+
*/
|
79 |
+
public function __destruct()
|
80 |
+
{
|
81 |
+
$this->_client->close();
|
82 |
+
}
|
83 |
+
|
84 |
+
/**
|
85 |
+
* @param float $timeout
|
86 |
+
* @return $this
|
87 |
+
*/
|
88 |
+
public function setClientTimeout($timeout)
|
89 |
+
{
|
90 |
+
$this->_timeout = $timeout;
|
91 |
+
return $this;
|
92 |
+
}
|
93 |
+
|
94 |
+
/**
|
95 |
+
* @param string $persistent
|
96 |
+
* @return $this
|
97 |
+
*/
|
98 |
+
public function setClientPersistent($persistent)
|
99 |
+
{
|
100 |
+
$this->_persistent = $persistent;
|
101 |
+
return $this;
|
102 |
+
}
|
103 |
+
|
104 |
+
/**
|
105 |
+
* @param int $db
|
106 |
+
* @return $this
|
107 |
+
*/
|
108 |
+
public function setClientDatabase($db)
|
109 |
+
{
|
110 |
+
$this->_db = $db;
|
111 |
+
return $this;
|
112 |
+
}
|
113 |
+
|
114 |
+
/**
|
115 |
+
* @param null|string $password
|
116 |
+
* @return $this
|
117 |
+
*/
|
118 |
+
public function setClientPassword($password)
|
119 |
+
{
|
120 |
+
$this->_password = $password;
|
121 |
+
return $this;
|
122 |
+
}
|
123 |
+
|
124 |
+
/**
|
125 |
+
* @return Credis_Sentinel
|
126 |
+
* @deprecated
|
127 |
+
*/
|
128 |
+
public function forceStandalone()
|
129 |
+
{
|
130 |
+
$this->_standAlone = true;
|
131 |
+
return $this;
|
132 |
+
}
|
133 |
+
|
134 |
+
/**
|
135 |
+
* Discover the master node automatically and return an instance of Credis_Client that connects to the master
|
136 |
+
*
|
137 |
+
* @param string $name
|
138 |
+
* @return Credis_Client
|
139 |
+
* @throws CredisException
|
140 |
+
*/
|
141 |
+
public function createMasterClient($name)
|
142 |
+
{
|
143 |
+
$master = $this->getMasterAddressByName($name);
|
144 |
+
if(!isset($master[0]) || !isset($master[1])){
|
145 |
+
throw new CredisException('Master not found');
|
146 |
+
}
|
147 |
+
return new Credis_Client($master[0], $master[1], $this->_timeout, $this->_persistent, $this->_db, $this->_password);
|
148 |
+
}
|
149 |
+
|
150 |
+
/**
|
151 |
+
* If a Credis_Client object exists for a master, return it. Otherwise create one and return it
|
152 |
+
* @param string $name
|
153 |
+
* @return Credis_Client
|
154 |
+
*/
|
155 |
+
public function getMasterClient($name)
|
156 |
+
{
|
157 |
+
if(!isset($this->_master[$name])){
|
158 |
+
$this->_master[$name] = $this->createMasterClient($name);
|
159 |
+
}
|
160 |
+
return $this->_master[$name];
|
161 |
+
}
|
162 |
+
|
163 |
+
/**
|
164 |
+
* Discover the slave nodes automatically and return an array of Credis_Client objects
|
165 |
+
*
|
166 |
+
* @param string $name
|
167 |
+
* @return Credis_Client[]
|
168 |
+
* @throws CredisException
|
169 |
+
*/
|
170 |
+
public function createSlaveClients($name)
|
171 |
+
{
|
172 |
+
$slaves = $this->slaves($name);
|
173 |
+
$workingSlaves = array();
|
174 |
+
foreach($slaves as $slave) {
|
175 |
+
if(!isset($slave[9])){
|
176 |
+
throw new CredisException('Can\' retrieve slave status');
|
177 |
+
}
|
178 |
+
if(!strstr($slave[9],'s_down') && !strstr($slave[9],'disconnected')) {
|
179 |
+
$workingSlaves[] = new Credis_Client($slave[3], $slave[5], $this->_timeout, $this->_persistent, $this->_db, $this->_password);
|
180 |
+
}
|
181 |
+
}
|
182 |
+
return $workingSlaves;
|
183 |
+
}
|
184 |
+
|
185 |
+
/**
|
186 |
+
* If an array of Credis_Client objects exist for a set of slaves, return them. Otherwise create and return them
|
187 |
+
* @param string $name
|
188 |
+
* @return Credis_Client[]
|
189 |
+
*/
|
190 |
+
public function getSlaveClients($name)
|
191 |
+
{
|
192 |
+
if(!isset($this->_slaves[$name])){
|
193 |
+
$this->_slaves[$name] = $this->createSlaveClients($name);
|
194 |
+
}
|
195 |
+
return $this->_slaves[$name];
|
196 |
+
}
|
197 |
+
|
198 |
+
/**
|
199 |
+
* Returns a Redis cluster object containing a random slave and the master
|
200 |
+
* When $selectRandomSlave is true, only one random slave is passed.
|
201 |
+
* When $selectRandomSlave is false, all clients are passed and hashing is applied in Credis_Cluster
|
202 |
+
* When $writeOnly is false, the master server will also be used for read commands.
|
203 |
+
*
|
204 |
+
* @param string $name
|
205 |
+
* @param int $db
|
206 |
+
* @param int $replicas
|
207 |
+
* @param bool $selectRandomSlave
|
208 |
+
* @param bool $writeOnly
|
209 |
+
* @return Credis_Cluster
|
210 |
+
* @throws CredisException
|
211 |
+
* @deprecated
|
212 |
+
*/
|
213 |
+
public function createCluster($name, $db=0, $replicas=128, $selectRandomSlave=true, $writeOnly=false)
|
214 |
+
{
|
215 |
+
$clients = array();
|
216 |
+
$workingClients = array();
|
217 |
+
$master = $this->master($name);
|
218 |
+
if(strstr($master[9],'s_down') || strstr($master[9],'disconnected')) {
|
219 |
+
throw new CredisException('The master is down');
|
220 |
+
}
|
221 |
+
$slaves = $this->slaves($name);
|
222 |
+
foreach($slaves as $slave){
|
223 |
+
if(!strstr($slave[9],'s_down') && !strstr($slave[9],'disconnected')) {
|
224 |
+
$workingClients[] = array('host'=>$slave[3],'port'=>$slave[5],'master'=>false,'db'=>$db,'password'=>$this->_password);
|
225 |
+
}
|
226 |
+
}
|
227 |
+
if(count($workingClients)>0){
|
228 |
+
if($selectRandomSlave){
|
229 |
+
if(!$writeOnly){
|
230 |
+
$workingClients[] = array('host'=>$master[3],'port'=>$master[5],'master'=>false,'db'=>$db,'password'=>$this->_password);
|
231 |
+
}
|
232 |
+
$clients[] = $workingClients[rand(0,count($workingClients)-1)];
|
233 |
+
} else {
|
234 |
+
$clients = $workingClients;
|
235 |
+
}
|
236 |
+
}
|
237 |
+
$clients[] = array('host'=>$master[3],'port'=>$master[5], 'db'=>$db ,'master'=>true,'write_only'=>$writeOnly,'password'=>$this->_password);
|
238 |
+
return new Credis_Cluster($clients,$replicas,$this->_standAlone);
|
239 |
+
}
|
240 |
+
|
241 |
+
/**
|
242 |
+
* If a Credis_Cluster object exists, return it. Otherwise create one and return it.
|
243 |
+
* @param string $name
|
244 |
+
* @param int $db
|
245 |
+
* @param int $replicas
|
246 |
+
* @param bool $selectRandomSlave
|
247 |
+
* @param bool $writeOnly
|
248 |
+
* @return Credis_Cluster
|
249 |
+
* @deprecated
|
250 |
+
*/
|
251 |
+
public function getCluster($name, $db=0, $replicas=128, $selectRandomSlave=true, $writeOnly=false)
|
252 |
+
{
|
253 |
+
if(!isset($this->_cluster[$name])){
|
254 |
+
$this->_cluster[$name] = $this->createCluster($name, $db, $replicas, $selectRandomSlave, $writeOnly);
|
255 |
+
}
|
256 |
+
return $this->_cluster[$name];
|
257 |
+
}
|
258 |
+
|
259 |
+
/**
|
260 |
+
* Catch-all method
|
261 |
+
* @param string $name
|
262 |
+
* @param array $args
|
263 |
+
* @return mixed
|
264 |
+
*/
|
265 |
+
public function __call($name, $args)
|
266 |
+
{
|
267 |
+
array_unshift($args,$name);
|
268 |
+
return call_user_func(array($this->_client,'sentinel'),$args);
|
269 |
+
}
|
270 |
+
|
271 |
+
/**
|
272 |
+
* get information block for the sentinel instance
|
273 |
+
*
|
274 |
+
* @param string|NUll $section
|
275 |
+
*
|
276 |
+
* @return array
|
277 |
+
*/
|
278 |
+
public function info($section = null)
|
279 |
+
{
|
280 |
+
if ($section)
|
281 |
+
{
|
282 |
+
return $this->_client->info($section);
|
283 |
+
}
|
284 |
+
return $this->_client->info();
|
285 |
+
}
|
286 |
+
|
287 |
+
/**
|
288 |
+
* Return information about all registered master servers
|
289 |
+
* @return mixed
|
290 |
+
*/
|
291 |
+
public function masters()
|
292 |
+
{
|
293 |
+
return $this->_client->sentinel('masters');
|
294 |
+
}
|
295 |
+
|
296 |
+
/**
|
297 |
+
* Return all information for slaves that are associated with a single master
|
298 |
+
* @param string $name
|
299 |
+
* @return mixed
|
300 |
+
*/
|
301 |
+
public function slaves($name)
|
302 |
+
{
|
303 |
+
return $this->_client->sentinel('slaves',$name);
|
304 |
+
}
|
305 |
+
|
306 |
+
/**
|
307 |
+
* Get the information for a specific master
|
308 |
+
* @param string $name
|
309 |
+
* @return mixed
|
310 |
+
*/
|
311 |
+
public function master($name)
|
312 |
+
{
|
313 |
+
return $this->_client->sentinel('master',$name);
|
314 |
+
}
|
315 |
+
|
316 |
+
/**
|
317 |
+
* Get the hostname and port for a specific master
|
318 |
+
* @param string $name
|
319 |
+
* @return mixed
|
320 |
+
*/
|
321 |
+
public function getMasterAddressByName($name)
|
322 |
+
{
|
323 |
+
return $this->_client->sentinel('get-master-addr-by-name',$name);
|
324 |
+
}
|
325 |
+
|
326 |
+
/**
|
327 |
+
* Check if the Sentinel is still responding
|
328 |
+
* @return string|Credis_Client
|
329 |
+
*/
|
330 |
+
public function ping()
|
331 |
+
{
|
332 |
+
return $this->_client->ping();
|
333 |
+
}
|
334 |
+
|
335 |
+
/**
|
336 |
+
* Perform an auto-failover which will re-elect another master and make the current master a slave
|
337 |
+
* @param string $name
|
338 |
+
* @return mixed
|
339 |
+
*/
|
340 |
+
public function failover($name)
|
341 |
+
{
|
342 |
+
return $this->_client->sentinel('failover',$name);
|
343 |
+
}
|
344 |
+
|
345 |
+
/**
|
346 |
+
* @return string
|
347 |
+
*/
|
348 |
+
public function getHost()
|
349 |
+
{
|
350 |
+
return $this->_client->getHost();
|
351 |
+
}
|
352 |
+
|
353 |
+
/**
|
354 |
+
* @return int
|
355 |
+
*/
|
356 |
+
public function getPort()
|
357 |
+
{
|
358 |
+
return $this->_client->getPort();
|
359 |
+
}
|
360 |
+
}
|
dependencies/vendor/colinmollenhour/credis/phpunit_local.sh
ADDED
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/usr/bin/env bash
|
2 |
+
|
3 |
+
# This script runs unit tests locally in environment similar to Travis-CI
|
4 |
+
# It runs tests in different PHP versions with suitable PHPUnite version.
|
5 |
+
#
|
6 |
+
# You can see results of unit tests execution in console.
|
7 |
+
# Also all execution logs are saved to files phpunit_<date-time>.log
|
8 |
+
#
|
9 |
+
# Prerequisites for running unit tests on local machine:
|
10 |
+
# - docker
|
11 |
+
# - docker-compose
|
12 |
+
#
|
13 |
+
# You can find definition of all test environments in folder testenv/
|
14 |
+
# This folder is not automatically synced with .travis.yml
|
15 |
+
# If you add new PHP version to .travis.yml then you'll need to adjust files in testenv/
|
16 |
+
|
17 |
+
cd testenv
|
18 |
+
|
19 |
+
# build containers and run tests
|
20 |
+
docker-compose build && docker-compose up
|
21 |
+
|
22 |
+
# save logs to log file
|
23 |
+
docker-compose logs --no-color --timestamps | sort >"../phpunit_$(date '+%Y%m%d-%H%M%S').log"
|
24 |
+
|
25 |
+
# remove containers
|
26 |
+
docker-compose rm -f
|
dependencies/vendor/colinmollenhour/credis/testenv/env/php-5.5/Dockerfile
ADDED
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
FROM php:5.5
|
2 |
+
ENV phpunit_verison 4.8
|
3 |
+
ENV redis_version 4.0.11
|
4 |
+
|
5 |
+
RUN apt-get update && \
|
6 |
+
apt-get install -y wget
|
7 |
+
|
8 |
+
RUN wget https://phar.phpunit.de/phpunit-${phpunit_verison}.phar && \
|
9 |
+
chmod +x phpunit-${phpunit_verison}.phar && \
|
10 |
+
mv phpunit-${phpunit_verison}.phar /usr/local/bin/phpunit
|
11 |
+
|
12 |
+
# install php extension
|
13 |
+
RUN yes '' | pecl install -f redis && \
|
14 |
+
docker-php-ext-enable redis
|
15 |
+
|
16 |
+
# install redis server
|
17 |
+
RUN wget http://download.redis.io/releases/redis-${redis_version}.tar.gz && \
|
18 |
+
tar -xzf redis-${redis_version}.tar.gz && \
|
19 |
+
make -s -C redis-${redis_version} -j
|
20 |
+
|
21 |
+
CMD PATH=$PATH:/usr/local/bin/:/redis-${redis_version}/src/ && \
|
22 |
+
cp -rp /src /app && \
|
23 |
+
cd /app && \
|
24 |
+
phpunit
|
dependencies/vendor/colinmollenhour/credis/testenv/env/php-5.6/Dockerfile
ADDED
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
FROM php:5.6
|
2 |
+
ENV phpunit_verison 5.7
|
3 |
+
ENV redis_version 4.0.11
|
4 |
+
|
5 |
+
RUN apt-get update && \
|
6 |
+
apt-get install -y wget
|
7 |
+
|
8 |
+
RUN wget https://phar.phpunit.de/phpunit-${phpunit_verison}.phar && \
|
9 |
+
chmod +x phpunit-${phpunit_verison}.phar && \
|
10 |
+
mv phpunit-${phpunit_verison}.phar /usr/local/bin/phpunit
|
11 |
+
|
12 |
+
# install php extension
|
13 |
+
RUN yes '' | pecl install -f redis && \
|
14 |
+
docker-php-ext-enable redis
|
15 |
+
|
16 |
+
# install redis server
|
17 |
+
RUN wget http://download.redis.io/releases/redis-${redis_version}.tar.gz && \
|
18 |
+
tar -xzf redis-${redis_version}.tar.gz && \
|
19 |
+
make -s -C redis-${redis_version} -j
|
20 |
+
|
21 |
+
CMD PATH=$PATH:/usr/local/bin/:/redis-${redis_version}/src/ && \
|
22 |
+
cp -rp /src /app && \
|
23 |
+
cd /app && \
|
24 |
+
phpunit
|
dependencies/vendor/colinmollenhour/credis/testenv/env/php-7.0/Dockerfile
ADDED
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
FROM php:7.0
|
2 |
+
ENV phpunit_verison 6.4
|
3 |
+
ENV redis_version 4.0.11
|
4 |
+
|
5 |
+
RUN apt-get update && \
|
6 |
+
apt-get install -y wget
|
7 |
+
|
8 |
+
RUN wget https://phar.phpunit.de/phpunit-${phpunit_verison}.phar && \
|
9 |
+
chmod +x phpunit-${phpunit_verison}.phar && \
|
10 |
+
mv phpunit-${phpunit_verison}.phar /usr/local/bin/phpunit
|
11 |
+
|
12 |
+
# install php extension
|
13 |
+
RUN yes '' | pecl install -f redis && \
|
14 |
+
docker-php-ext-enable redis
|
15 |
+
|
16 |
+
# install redis server
|
17 |
+
RUN wget http://download.redis.io/releases/redis-${redis_version}.tar.gz && \
|
18 |
+
tar -xzf redis-${redis_version}.tar.gz && \
|
19 |
+
make -s -C redis-${redis_version} -j
|
20 |
+
|
21 |
+
CMD PATH=$PATH:/usr/local/bin/:/redis-${redis_version}/src/ && \
|
22 |
+
cp -rp /src /app && \
|
23 |
+
cd /app && \
|
24 |
+
phpunit
|
dependencies/vendor/colinmollenhour/credis/testenv/env/php-7.1/Dockerfile
ADDED
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
FROM php:7.1
|
2 |
+
ENV phpunit_verison 6.4
|
3 |
+
ENV redis_version 4.0.11
|
4 |
+
|
5 |
+
RUN apt-get update && \
|
6 |
+
apt-get install -y wget
|
7 |
+
|
8 |
+
RUN wget https://phar.phpunit.de/phpunit-${phpunit_verison}.phar && \
|
9 |
+
chmod +x phpunit-${phpunit_verison}.phar && \
|
10 |
+
mv phpunit-${phpunit_verison}.phar /usr/local/bin/phpunit
|
11 |
+
|
12 |
+
# install php extension
|
13 |
+
RUN yes '' | pecl install -f redis && \
|
14 |
+
docker-php-ext-enable redis
|
15 |
+
|
16 |
+
# install redis server
|
17 |
+
RUN wget http://download.redis.io/releases/redis-${redis_version}.tar.gz && \
|
18 |
+
tar -xzf redis-${redis_version}.tar.gz && \
|
19 |
+
make -s -C redis-${redis_version} -j
|
20 |
+
|
21 |
+
CMD PATH=$PATH:/usr/local/bin/:/redis-${redis_version}/src/ && \
|
22 |
+
cp -rp /src /app && \
|
23 |
+
cd /app && \
|
24 |
+
phpunit
|
dependencies/vendor/colinmollenhour/credis/testenv/env/php-7.2/Dockerfile
ADDED
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
FROM php:7.2
|
2 |
+
ENV phpunit_verison 7.5
|
3 |
+
ENV redis_version 4.0.11
|
4 |
+
|
5 |
+
RUN apt-get update && \
|
6 |
+
apt-get install -y wget
|
7 |
+
|
8 |
+
RUN wget https://phar.phpunit.de/phpunit-${phpunit_verison}.phar && \
|
9 |
+
chmod +x phpunit-${phpunit_verison}.phar && \
|
10 |
+
mv phpunit-${phpunit_verison}.phar /usr/local/bin/phpunit
|
11 |
+
|
12 |
+
# install php extension
|
13 |
+
RUN yes '' | pecl install -f redis && \
|
14 |
+
docker-php-ext-enable redis
|
15 |
+
|
16 |
+
# install redis server
|
17 |
+
RUN wget http://download.redis.io/releases/redis-${redis_version}.tar.gz && \
|
18 |
+
tar -xzf redis-${redis_version}.tar.gz && \
|
19 |
+
make -s -C redis-${redis_version} -j
|
20 |
+
|
21 |
+
CMD PATH=$PATH:/usr/local/bin/:/redis-${redis_version}/src/ && \
|
22 |
+
cp -rp /src /app && \
|
23 |
+
cd /app && \
|
24 |
+
phpunit
|
dependencies/vendor/colinmollenhour/credis/testenv/env/php-7.3/Dockerfile
ADDED
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
FROM php:7.3
|
2 |
+
ENV phpunit_verison 7.5
|
3 |
+
ENV redis_version 4.0.11
|
4 |
+
|
5 |
+
RUN apt-get update && \
|
6 |
+
apt-get install -y wget
|
7 |
+
|
8 |
+
RUN wget https://phar.phpunit.de/phpunit-${phpunit_verison}.phar && \
|
9 |
+
chmod +x phpunit-${phpunit_verison}.phar && \
|
10 |
+
mv phpunit-${phpunit_verison}.phar /usr/local/bin/phpunit
|
11 |
+
|
12 |
+
# install php extension
|
13 |
+
RUN yes '' | pecl install -f redis && \
|
14 |
+
docker-php-ext-enable redis
|
15 |
+
|
16 |
+
# install redis server
|
17 |
+
RUN wget http://download.redis.io/releases/redis-${redis_version}.tar.gz && \
|
18 |
+
tar -xzf redis-${redis_version}.tar.gz && \
|
19 |
+
make -s -C redis-${redis_version} -j
|
20 |
+
|
21 |
+
CMD PATH=$PATH:/usr/local/bin/:/redis-${redis_version}/src/ && \
|
22 |
+
cp -rp /src /app && \
|
23 |
+
cd /app && \
|
24 |
+
phpunit
|
dependencies/vendor/colinmollenhour/credis/tests/CredisClusterTest.php
ADDED
@@ -0,0 +1,211 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
require_once dirname(__FILE__).'/../Client.php';
|
4 |
+
require_once dirname(__FILE__).'/../Cluster.php';
|
5 |
+
require_once dirname(__FILE__).'/CredisTestCommon.php';
|
6 |
+
|
7 |
+
class CredisClusterTest extends CredisTestCommon
|
8 |
+
{
|
9 |
+
/** @var Credis_Cluster */
|
10 |
+
protected $cluster;
|
11 |
+
|
12 |
+
protected function setUp()
|
13 |
+
{
|
14 |
+
parent::setUp();
|
15 |
+
|
16 |
+
$clients = array_slice($this->redisConfig,0,4);
|
17 |
+
$this->cluster = new Credis_Cluster($clients,2,$this->useStandalone);
|
18 |
+
}
|
19 |
+
|
20 |
+
protected function tearDown()
|
21 |
+
{
|
22 |
+
if($this->cluster) {
|
23 |
+
$this->cluster->flushAll();
|
24 |
+
foreach($this->cluster->clients() as $client){
|
25 |
+
if($client->isConnected()) {
|
26 |
+
$client->close();
|
27 |
+
}
|
28 |
+
}
|
29 |
+
$this->cluster = NULL;
|
30 |
+
}
|
31 |
+
}
|
32 |
+
|
33 |
+
public function testKeyHashing()
|
34 |
+
{
|
35 |
+
$this->tearDown();
|
36 |
+
$this->cluster = new Credis_Cluster(array_slice($this->redisConfig, 0, 3), 2, $this->useStandalone);
|
37 |
+
$keys = array();
|
38 |
+
$lines = explode("\n", file_get_contents("keys.test"));
|
39 |
+
foreach ($lines as $line) {
|
40 |
+
$pair = explode(':', trim($line));
|
41 |
+
if (count($pair) >= 2) {
|
42 |
+
$keys[$pair[0]] = $pair[1];
|
43 |
+
}
|
44 |
+
}
|
45 |
+
foreach ($keys as $key => $value) {
|
46 |
+
$this->assertTrue($this->cluster->set($key, $value));
|
47 |
+
}
|
48 |
+
$this->cluster = new Credis_Cluster(array_slice($this->redisConfig, 0, 4), 2, true, $this->useStandalone);
|
49 |
+
$hits = 0;
|
50 |
+
foreach ($keys as $key => $value) {
|
51 |
+
if ($this->cluster->all('get',$key)) {
|
52 |
+
$hits++;
|
53 |
+
}
|
54 |
+
}
|
55 |
+
$this->assertEquals(count($keys),$hits);
|
56 |
+
}
|
57 |
+
public function testAlias()
|
58 |
+
{
|
59 |
+
$slicedConfig = array_slice($this->redisConfig, 0, 4);
|
60 |
+
foreach($slicedConfig as $config) {
|
61 |
+
$this->assertEquals($config['port'],$this->cluster->client($config['alias'])->getPort());
|
62 |
+
}
|
63 |
+
foreach($slicedConfig as $offset => $config) {
|
64 |
+
$this->assertEquals($config['port'],$this->cluster->client($offset)->getPort());
|
65 |
+
}
|
66 |
+
$alias = "non-existent-alias";
|
67 |
+
$this->setExpectedExceptionShim('CredisException',"Client $alias does not exist.");
|
68 |
+
$this->cluster->client($alias);
|
69 |
+
}
|
70 |
+
public function testMasterSlave()
|
71 |
+
{
|
72 |
+
$this->tearDown();
|
73 |
+
$this->cluster = new Credis_Cluster(array($this->redisConfig[0],$this->redisConfig[6]), 2, $this->useStandalone);
|
74 |
+
$this->assertTrue($this->cluster->client('master')->set('key','value'));
|
75 |
+
$this->waitForSlaveReplication();
|
76 |
+
$this->assertEquals('value',$this->cluster->client('slave')->get('key'));
|
77 |
+
$this->assertEquals('value',$this->cluster->get('key'));
|
78 |
+
try
|
79 |
+
{
|
80 |
+
$this->cluster->client('slave')->set('key2', 'value');
|
81 |
+
$this->fail('Writing to readonly slave');
|
82 |
+
}
|
83 |
+
catch(CredisException $e)
|
84 |
+
{
|
85 |
+
}
|
86 |
+
|
87 |
+
$this->tearDown();
|
88 |
+
$writeOnlyConfig = $this->redisConfig[0];
|
89 |
+
$writeOnlyConfig['write_only'] = true;
|
90 |
+
$this->cluster = new Credis_Cluster(array($writeOnlyConfig,$this->redisConfig[6]), 2, $this->useStandalone);
|
91 |
+
$this->assertTrue($this->cluster->client('master')->set('key','value'));
|
92 |
+
$this->waitForSlaveReplication();
|
93 |
+
$this->assertEquals('value',$this->cluster->client('slave')->get('key'));
|
94 |
+
$this->assertEquals('value',$this->cluster->get('key'));
|
95 |
+
$this->setExpectedExceptionShim('CredisException');
|
96 |
+
$this->assertFalse($this->cluster->client('slave')->set('key2','value'));
|
97 |
+
}
|
98 |
+
public function testMasterWithoutSlavesAndWriteOnlyFlag()
|
99 |
+
{
|
100 |
+
$this->tearDown();
|
101 |
+
$writeOnlyConfig = $this->redisConfig[0];
|
102 |
+
$writeOnlyConfig['write_only'] = true;
|
103 |
+
$this->cluster = new Credis_Cluster(array($writeOnlyConfig),2,$this->useStandalone);
|
104 |
+
$this->assertTrue($this->cluster->set('key','value'));
|
105 |
+
$this->assertEquals('value',$this->cluster->get('key'));
|
106 |
+
}
|
107 |
+
public function testDontHashForCodeCoverage()
|
108 |
+
{
|
109 |
+
$this->assertInternalType('array',$this->cluster->info());
|
110 |
+
}
|
111 |
+
public function testByHash()
|
112 |
+
{
|
113 |
+
$this->cluster->set('key','value');
|
114 |
+
$this->assertEquals(6379,$this->cluster->byHash('key')->getPort());
|
115 |
+
}
|
116 |
+
public function testRwsplit()
|
117 |
+
{
|
118 |
+
$readOnlyCommands = array(
|
119 |
+
'EXISTS',
|
120 |
+
'TYPE',
|
121 |
+
'KEYS',
|
122 |
+
'SCAN',
|
123 |
+
'RANDOMKEY',
|
124 |
+
'TTL',
|
125 |
+
'GET',
|
126 |
+
'MGET',
|
127 |
+
'SUBSTR',
|
128 |
+
'STRLEN',
|
129 |
+
'GETRANGE',
|
130 |
+
'GETBIT',
|
131 |
+
'LLEN',
|
132 |
+
'LRANGE',
|
133 |
+
'LINDEX',
|
134 |
+
'SCARD',
|
135 |
+
'SISMEMBER',
|
136 |
+
'SINTER',
|
137 |
+
'SUNION',
|
138 |
+
'SDIFF',
|
139 |
+
'SMEMBERS',
|
140 |
+
'SSCAN',
|
141 |
+
'SRANDMEMBER',
|
142 |
+
'ZRANGE',
|
143 |
+
'ZREVRANGE',
|
144 |
+
'ZRANGEBYSCORE',
|
145 |
+
'ZREVRANGEBYSCORE',
|
146 |
+
'ZCARD',
|
147 |
+
'ZSCORE',
|
148 |
+
'ZCOUNT',
|
149 |
+
'ZRANK',
|
150 |
+
'ZREVRANK',
|
151 |
+
'ZSCAN',
|
152 |
+
'HGET',
|
153 |
+
'HMGET',
|
154 |
+
'HEXISTS',
|
155 |
+
'HLEN',
|
156 |
+
'HKEYS',
|
157 |
+
'HVALS',
|
158 |
+
'HGETALL',
|
159 |
+
'HSCAN',
|
160 |
+
'PING',
|
161 |
+
'AUTH',
|
162 |
+
'SELECT',
|
163 |
+
'ECHO',
|
164 |
+
'QUIT',
|
165 |
+
'OBJECT',
|
166 |
+
'BITCOUNT',
|
167 |
+
'TIME',
|
168 |
+
'SORT'
|
169 |
+
);
|
170 |
+
foreach($readOnlyCommands as $command){
|
171 |
+
$this->assertTrue($this->cluster->isReadOnlyCommand($command));
|
172 |
+
}
|
173 |
+
$this->assertFalse($this->cluster->isReadOnlyCommand("SET"));
|
174 |
+
$this->assertFalse($this->cluster->isReadOnlyCommand("HDEL"));
|
175 |
+
$this->assertFalse($this->cluster->isReadOnlyCommand("RPUSH"));
|
176 |
+
$this->assertFalse($this->cluster->isReadOnlyCommand("SMOVE"));
|
177 |
+
$this->assertFalse($this->cluster->isReadOnlyCommand("ZADD"));
|
178 |
+
}
|
179 |
+
public function testCredisClientInstancesInConstructor()
|
180 |
+
{
|
181 |
+
$this->tearDown();
|
182 |
+
$two = new Credis_Client($this->redisConfig[1]['host'], $this->redisConfig[1]['port']);
|
183 |
+
$three = new Credis_Client($this->redisConfig[2]['host'], $this->redisConfig[2]['port']);
|
184 |
+
$four = new Credis_Client($this->redisConfig[3]['host'], $this->redisConfig[3]['port']);
|
185 |
+
$this->cluster = new Credis_Cluster(array($two,$three,$four),2,$this->useStandalone);
|
186 |
+
$this->assertTrue($this->cluster->set('key','value'));
|
187 |
+
$this->assertEquals('value',$this->cluster->get('key'));
|
188 |
+
$this->setExpectedExceptionShim('CredisException','Server should either be an array or an instance of Credis_Client');
|
189 |
+
new Credis_Cluster(array(new stdClass()),2,$this->useStandalone);
|
190 |
+
}
|
191 |
+
public function testSetMasterClient()
|
192 |
+
{
|
193 |
+
$this->tearDown();
|
194 |
+
$master = new Credis_Client($this->redisConfig[0]['host'], $this->redisConfig[0]['port']);
|
195 |
+
$slave = new Credis_Client($this->redisConfig[6]['host'], $this->redisConfig[6]['port']);
|
196 |
+
|
197 |
+
$this->cluster = new Credis_Cluster(array($slave),2,$this->useStandalone);
|
198 |
+
$this->assertInstanceOf('Credis_Cluster',$this->cluster->setMasterClient($master));
|
199 |
+
$this->assertCount(2,$this->cluster->clients());
|
200 |
+
$this->assertEquals($this->redisConfig[6]['port'], $this->cluster->client(0)->getPort());
|
201 |
+
$this->assertEquals($this->redisConfig[0]['port'], $this->cluster->client('master')->getPort());
|
202 |
+
|
203 |
+
$this->cluster = new Credis_Cluster(array($this->redisConfig[0]), 2, $this->useStandalone);
|
204 |
+
$this->assertInstanceOf('Credis_Cluster',$this->cluster->setMasterClient(new Credis_Client($this->redisConfig[1]['host'], $this->redisConfig[1]['port'])));
|
205 |
+
$this->assertEquals($this->redisConfig[0]['port'], $this->cluster->client('master')->getPort());
|
206 |
+
|
207 |
+
$this->cluster = new Credis_Cluster(array($slave),2,$this->useStandalone);
|
208 |
+
$this->assertInstanceOf('Credis_Cluster',$this->cluster->setMasterClient($master,true));
|
209 |
+
$this->assertCount(1,$this->cluster->clients());
|
210 |
+
}
|
211 |
+
}
|
dependencies/vendor/colinmollenhour/credis/tests/CredisSentinelTest.php
ADDED
@@ -0,0 +1,201 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
require_once dirname(__FILE__).'/../Client.php';
|
4 |
+
require_once dirname(__FILE__).'/../Cluster.php';
|
5 |
+
require_once dirname(__FILE__).'/../Sentinel.php';
|
6 |
+
require_once dirname(__FILE__).'/CredisTestCommon.php';
|
7 |
+
|
8 |
+
class CredisSentinelTest extends CredisTestCommon
|
9 |
+
{
|
10 |
+
/** @var Credis_Sentinel */
|
11 |
+
protected $sentinel;
|
12 |
+
|
13 |
+
protected $sentinelConfig;
|
14 |
+
|
15 |
+
protected function setUp()
|
16 |
+
{
|
17 |
+
parent::setUp();
|
18 |
+
if($this->sentinelConfig === NULL) {
|
19 |
+
$configFile = dirname(__FILE__).'/sentinel_config.json';
|
20 |
+
if( ! file_exists($configFile) || ! ($config = file_get_contents($configFile))) {
|
21 |
+
$this->markTestSkipped('Could not load '.$configFile);
|
22 |
+
return;
|
23 |
+
}
|
24 |
+
$this->sentinelConfig = json_decode($config);
|
25 |
+
}
|
26 |
+
|
27 |
+
$sentinelClient = new Credis_Client($this->sentinelConfig->host, $this->sentinelConfig->port);
|
28 |
+
$this->sentinel = new Credis_Sentinel($sentinelClient);
|
29 |
+
if($this->useStandalone) {
|
30 |
+
$this->sentinel->forceStandalone();
|
31 |
+
}
|
32 |
+
$this->waitForSlaveReplication();
|
33 |
+
}
|
34 |
+
|
35 |
+
public static function setUpBeforeClass()
|
36 |
+
{
|
37 |
+
parent::setUpBeforeClass();
|
38 |
+
if(preg_match('/^WIN/',strtoupper(PHP_OS))){
|
39 |
+
echo "\tredis-server redis-sentinel.conf --sentinel".PHP_EOL.PHP_EOL;
|
40 |
+
} else {
|
41 |
+
sleep(2);
|
42 |
+
chdir(__DIR__);
|
43 |
+
copy('redis-sentinel.conf','redis-sentinel.conf.bak');
|
44 |
+
exec('redis-server redis-sentinel.conf --sentinel');
|
45 |
+
// wait for redis to initialize
|
46 |
+
sleep(1);
|
47 |
+
}
|
48 |
+
}
|
49 |
+
|
50 |
+
public static function tearDownAfterClass()
|
51 |
+
{
|
52 |
+
parent::tearDownAfterClass();
|
53 |
+
if(preg_match('/^WIN/',strtoupper(PHP_OS))){
|
54 |
+
echo "Please kill all Redis instances manually:".PHP_EOL;
|
55 |
+
} else {
|
56 |
+
chdir(__DIR__);
|
57 |
+
@unlink('redis-sentinel.conf');
|
58 |
+
@copy('redis-sentinel.conf.bak','redis-sentinel.conf');
|
59 |
+
}
|
60 |
+
}
|
61 |
+
|
62 |
+
protected function tearDown()
|
63 |
+
{
|
64 |
+
if($this->sentinel) {
|
65 |
+
$this->sentinel = NULL;
|
66 |
+
}
|
67 |
+
}
|
68 |
+
public function testMasterClient()
|
69 |
+
{
|
70 |
+
$master = $this->sentinel->getMasterClient($this->sentinelConfig->clustername);
|
71 |
+
$this->assertInstanceOf('Credis_Client',$master);
|
72 |
+
$this->assertEquals($this->redisConfig[0]['port'],$master->getPort());
|
73 |
+
$this->setExpectedExceptionShim('CredisException','Master not found');
|
74 |
+
$this->sentinel->getMasterClient('non-existing-cluster');
|
75 |
+
}
|
76 |
+
public function testMasters()
|
77 |
+
{
|
78 |
+
$masters = $this->sentinel->masters();
|
79 |
+
$this->assertInternalType('array',$masters);
|
80 |
+
$this->assertCount(2,$masters);
|
81 |
+
$this->assertArrayHasKey(0,$masters);
|
82 |
+
$this->assertArrayHasKey(1,$masters);
|
83 |
+
$this->assertArrayHasKey(1,$masters[0]);
|
84 |
+
$this->assertArrayHasKey(1,$masters[1]);
|
85 |
+
$this->assertArrayHasKey(5,$masters[1]);
|
86 |
+
if($masters[0][1] == 'masterdown'){
|
87 |
+
$this->assertEquals($this->sentinelConfig->clustername,$masters[1][1]);
|
88 |
+
$this->assertEquals($this->redisConfig[0]['port'],$masters[1][5]);
|
89 |
+
} else {
|
90 |
+
$this->assertEquals('masterdown',$masters[1][1]);
|
91 |
+
$this->assertEquals($this->sentinelConfig->clustername,$masters[0][1]);
|
92 |
+
$this->assertEquals($this->redisConfig[0]['port'],$masters[0][5]);
|
93 |
+
}
|
94 |
+
}
|
95 |
+
public function testMaster()
|
96 |
+
{
|
97 |
+
$master = $this->sentinel->master($this->sentinelConfig->clustername);
|
98 |
+
$this->assertInternalType('array',$master);
|
99 |
+
$this->assertArrayHasKey(1,$master);
|
100 |
+
$this->assertArrayHasKey(5,$master);
|
101 |
+
$this->assertEquals($this->sentinelConfig->clustername,$master[1]);
|
102 |
+
$this->assertEquals($this->redisConfig[0]['port'],$master[5]);
|
103 |
+
|
104 |
+
$this->setExpectedExceptionShim('CredisException','No such master with that name');
|
105 |
+
$this->sentinel->master('non-existing-cluster');
|
106 |
+
}
|
107 |
+
public function testSlaveClient()
|
108 |
+
{
|
109 |
+
$slaves = $this->sentinel->getSlaveClients($this->sentinelConfig->clustername);
|
110 |
+
$this->assertInternalType('array',$slaves);
|
111 |
+
$this->assertCount(1,$slaves);
|
112 |
+
foreach($slaves as $slave){
|
113 |
+
$this->assertInstanceOf('Credis_Client',$slave);
|
114 |
+
}
|
115 |
+
$this->setExpectedExceptionShim('CredisException','No such master with that name');
|
116 |
+
$this->sentinel->getSlaveClients('non-existing-cluster');
|
117 |
+
}
|
118 |
+
public function testSlaves()
|
119 |
+
{
|
120 |
+
$slaves = $this->sentinel->slaves($this->sentinelConfig->clustername);
|
121 |
+
$this->assertInternalType('array',$slaves);
|
122 |
+
$this->assertCount(1,$slaves);
|
123 |
+
$this->assertArrayHasKey(0,$slaves);
|
124 |
+
$this->assertArrayHasKey(5,$slaves[0]);
|
125 |
+
$this->assertEquals(6385,$slaves[0][5]);
|
126 |
+
|
127 |
+
$slaves = $this->sentinel->slaves('masterdown');
|
128 |
+
$this->assertInternalType('array',$slaves);
|
129 |
+
$this->assertCount(0,$slaves);
|
130 |
+
|
131 |
+
$this->setExpectedExceptionShim('CredisException','No such master with that name');
|
132 |
+
$this->sentinel->slaves('non-existing-cluster');
|
133 |
+
}
|
134 |
+
public function testNonExistingClusterNameWhenCreatingSlaves()
|
135 |
+
{
|
136 |
+
$this->setExpectedExceptionShim('CredisException','No such master with that name');
|
137 |
+
$this->sentinel->createSlaveClients('non-existing-cluster');
|
138 |
+
}
|
139 |
+
public function testCreateCluster()
|
140 |
+
{
|
141 |
+
$cluster = $this->sentinel->createCluster($this->sentinelConfig->clustername);
|
142 |
+
$this->assertInstanceOf('Credis_Cluster',$cluster);
|
143 |
+
$this->assertCount(2,$cluster->clients());
|
144 |
+
$cluster = $this->sentinel->createCluster($this->sentinelConfig->clustername,0,1,false);
|
145 |
+
$this->assertInstanceOf('Credis_Cluster',$cluster);
|
146 |
+
$this->assertCount(2,$cluster->clients());
|
147 |
+
$this->setExpectedExceptionShim('CredisException','The master is down');
|
148 |
+
$this->sentinel->createCluster($this->sentinelConfig->downclustername);
|
149 |
+
}
|
150 |
+
public function testGetCluster()
|
151 |
+
{
|
152 |
+
$cluster = $this->sentinel->getCluster($this->sentinelConfig->clustername);
|
153 |
+
$this->assertInstanceOf('Credis_Cluster',$cluster);
|
154 |
+
$this->assertCount(2,$cluster->clients());
|
155 |
+
}
|
156 |
+
public function testGetClusterOnDbNumber2()
|
157 |
+
{
|
158 |
+
$cluster = $this->sentinel->getCluster($this->sentinelConfig->clustername,2);
|
159 |
+
$this->assertInstanceOf('Credis_Cluster',$cluster);
|
160 |
+
$this->assertCount(2,$cluster->clients());
|
161 |
+
$clients = $cluster->clients();
|
162 |
+
$this->assertEquals(2,$clients[0]->getSelectedDb());
|
163 |
+
$this->assertEquals(2,$clients[1]->getSelectedDb());
|
164 |
+
}
|
165 |
+
public function testGetMasterAddressByName()
|
166 |
+
{
|
167 |
+
$address = $this->sentinel->getMasterAddressByName($this->sentinelConfig->clustername);
|
168 |
+
$this->assertInternalType('array',$address);
|
169 |
+
$this->assertCount(2,$address);
|
170 |
+
$this->assertArrayHasKey(0,$address);
|
171 |
+
$this->assertArrayHasKey(1,$address);
|
172 |
+
$this->assertEquals($this->redisConfig[0]['host'],$address[0]);
|
173 |
+
$this->assertEquals($this->redisConfig[0]['port'],$address[1]);
|
174 |
+
}
|
175 |
+
|
176 |
+
public function testPing()
|
177 |
+
{
|
178 |
+
$pong = $this->sentinel->ping();
|
179 |
+
$this->assertEquals("PONG",$pong);
|
180 |
+
}
|
181 |
+
|
182 |
+
public function testGetHostAndPort()
|
183 |
+
{
|
184 |
+
$host = 'localhost';
|
185 |
+
$port = '123456';
|
186 |
+
|
187 |
+
$client = $this->createMockShim('\Credis_Client');
|
188 |
+
$sentinel = new Credis_Sentinel($client);
|
189 |
+
|
190 |
+
$client->expects($this->once())->method('getHost')->willReturn($host);
|
191 |
+
$client->expects($this->once())->method('getPort')->willReturn($port);
|
192 |
+
|
193 |
+
$this->assertEquals($host, $sentinel->getHost());
|
194 |
+
$this->assertEquals($port, $sentinel->getPort());
|
195 |
+
}
|
196 |
+
public function testNonExistingMethod()
|
197 |
+
{
|
198 |
+
$this->setExpectedExceptionShim('CredisException','Unknown sentinel subcommand \'bla\'');
|
199 |
+
$this->sentinel->bla();
|
200 |
+
}
|
201 |
+
}
|
dependencies/vendor/colinmollenhour/credis/tests/CredisStandaloneClusterTest.php
ADDED
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
require_once dirname(__FILE__).'/CredisClusterTest.php';
|
4 |
+
|
5 |
+
class CredisStandaloneClusterTest extends CredisClusterTest
|
6 |
+
{
|
7 |
+
protected $useStandalone = TRUE;
|
8 |
+
protected function tearDown()
|
9 |
+
{
|
10 |
+
if($this->cluster) {
|
11 |
+
foreach($this->cluster->clients() as $client){
|
12 |
+
if($client->isConnected()) {
|
13 |
+
$client->close();
|
14 |
+
}
|
15 |
+
}
|
16 |
+
$this->cluster = NULL;
|
17 |
+
}
|
18 |
+
}
|
19 |
+
public function testMasterSlave()
|
20 |
+
{
|
21 |
+
$this->tearDown();
|
22 |
+
$this->cluster = new Credis_Cluster(array($this->redisConfig[0],$this->redisConfig[6]), 2, $this->useStandalone);
|
23 |
+
$this->assertTrue($this->cluster->client('master')->set('key','value'));
|
24 |
+
$this->waitForSlaveReplication();
|
25 |
+
$this->assertEquals('value',$this->cluster->client('slave')->get('key'));
|
26 |
+
$this->assertEquals('value',$this->cluster->get('key'));
|
27 |
+
$this->setExpectedExceptionShim('CredisException','READONLY You can\'t write against a read only replica.');
|
28 |
+
$this->cluster->client('slave')->set('key2','value');
|
29 |
+
}
|
30 |
+
}
|
dependencies/vendor/colinmollenhour/credis/tests/CredisStandaloneSentinelTest.php
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
require_once dirname(__FILE__).'/CredisSentinelTest.php';
|
4 |
+
|
5 |
+
class CredisStandaloneSentinelTest extends CredisSentinelTest
|
6 |
+
{
|
7 |
+
protected $useStandalone = TRUE;
|
8 |
+
}
|
dependencies/vendor/colinmollenhour/credis/tests/CredisStandaloneTest.php
ADDED
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
require_once dirname(__FILE__).'/CredisTest.php';
|
4 |
+
|
5 |
+
class CredisStandaloneTest extends CredisTest
|
6 |
+
{
|
7 |
+
protected $useStandalone = TRUE;
|
8 |
+
|
9 |
+
/**
|
10 |
+
* @group UnixSocket
|
11 |
+
*/
|
12 |
+
public function testInvalidPersistentConnectionOnUnixSocket()
|
13 |
+
{
|
14 |
+
$this->credis->close();
|
15 |
+
$this->credis = new Credis_Client('unix://'.realpath(__DIR__).'/redis.sock',0,null,'persistent');
|
16 |
+
$this->credis->forceStandalone();
|
17 |
+
//$this->setExpectedException('CredisException','Persistent connections to UNIX sockets are not supported in standalone mode.');
|
18 |
+
$this->credis->connect();
|
19 |
+
$this->assertTrue($this->credis->isConnected());
|
20 |
+
}
|
21 |
+
|
22 |
+
public function testPersistentConnectionsOnStandAloneTcpConnection()
|
23 |
+
{
|
24 |
+
$this->credis->close();
|
25 |
+
$this->credis = new Credis_Client('tcp://'.$this->redisConfig[0]['host'] . ':' . $this->redisConfig[0]['port'] . '/persistent');
|
26 |
+
$this->credis->forceStandalone();
|
27 |
+
$this->credis->set('key','value');
|
28 |
+
$this->assertEquals('value',$this->credis->get('key'));
|
29 |
+
}
|
30 |
+
|
31 |
+
public function testPersistentvsNonPersistent() {$this->assertTrue(true);}
|
32 |
+
|
33 |
+
public function testStandAloneArgumentsExtra()
|
34 |
+
{
|
35 |
+
$this->assertTrue($this->credis->hMSet('hash', array('field1' => 'value1', 'field2' => 'value2'), 'field3', 'value3'));
|
36 |
+
$this->assertEquals(array('field1' => 'value1', 'field2' => 'value2', 'field3' =>'value3'), $this->credis->hMGet('hash', array('field1','field2','field3')));
|
37 |
+
}
|
38 |
+
|
39 |
+
public function testStandAloneMultiPipelineThrowsException()
|
40 |
+
{
|
41 |
+
$this->setExpectedExceptionShim('CredisException','A pipeline is already in use and only one pipeline is supported.');
|
42 |
+
$this->credis->pipeline()->pipeline();
|
43 |
+
}
|
44 |
+
}
|
dependencies/vendor/colinmollenhour/credis/tests/CredisTest.php
ADDED
@@ -0,0 +1,793 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
require_once dirname(__FILE__).'/../Client.php';
|
4 |
+
require_once dirname(__FILE__).'/CredisTestCommon.php';
|
5 |
+
|
6 |
+
class CredisTest extends CredisTestCommon
|
7 |
+
{
|
8 |
+
/** @var Credis_Client */
|
9 |
+
protected $credis;
|
10 |
+
|
11 |
+
protected function setUp()
|
12 |
+
{
|
13 |
+
parent::setUp();
|
14 |
+
$this->credis = new Credis_Client($this->redisConfig[0]['host'], $this->redisConfig[0]['port'], $this->redisConfig[0]['timeout']);
|
15 |
+
if($this->useStandalone) {
|
16 |
+
$this->credis->forceStandalone();
|
17 |
+
}
|
18 |
+
$this->credis->flushDb();
|
19 |
+
}
|
20 |
+
protected function tearDown()
|
21 |
+
{
|
22 |
+
if($this->credis) {
|
23 |
+
$this->credis->close();
|
24 |
+
$this->credis = NULL;
|
25 |
+
}
|
26 |
+
}
|
27 |
+
public function testFlush()
|
28 |
+
{
|
29 |
+
$this->credis->set('foo','FOO');
|
30 |
+
$this->assertTrue($this->credis->flushDb());
|
31 |
+
$this->assertFalse($this->credis->get('foo'));
|
32 |
+
}
|
33 |
+
|
34 |
+
public function testReadTimeout()
|
35 |
+
{
|
36 |
+
$this->credis->setReadTimeout(0.0001);
|
37 |
+
try {
|
38 |
+
$this->credis->save();
|
39 |
+
$this->fail('Expected exception (read should timeout since disk sync should take longer than 0.0001 seconds).');
|
40 |
+
} catch(CredisException $e) {
|
41 |
+
}
|
42 |
+
$this->credis->setReadTimeout(10);
|
43 |
+
$this->assertTrue(true);
|
44 |
+
}
|
45 |
+
|
46 |
+
public function testPHPRedisReadTimeout()
|
47 |
+
{
|
48 |
+
try {
|
49 |
+
$this->credis->setReadTimeout(-1);
|
50 |
+
} catch(CredisException $e) {
|
51 |
+
$this->fail('setReadTimeout should accept -1 as timeout value');
|
52 |
+
}
|
53 |
+
try {
|
54 |
+
$this->credis->setReadTimeout(-2);
|
55 |
+
$this->fail('setReadTimeout should not accept values less than -1');
|
56 |
+
} catch(CredisException $e) {
|
57 |
+
}
|
58 |
+
$this->assertTrue(true);
|
59 |
+
}
|
60 |
+
|
61 |
+
public function testScalars()
|
62 |
+
{
|
63 |
+
// Basic get/set
|
64 |
+
$this->credis->set('foo','FOO');
|
65 |
+
$this->assertEquals('FOO', $this->credis->get('foo'));
|
66 |
+
$this->assertFalse($this->credis->get('nil'));
|
67 |
+
|
68 |
+
// exists support
|
69 |
+
$this->assertEquals($this->credis->exists('foo'), 1);
|
70 |
+
$this->assertEquals($this->credis->exists('nil'), 0);
|
71 |
+
|
72 |
+
// Empty string
|
73 |
+
$this->credis->set('empty','');
|
74 |
+
$this->assertEquals('', $this->credis->get('empty'));
|
75 |
+
|
76 |
+
// UTF-8 characters
|
77 |
+
$utf8str = str_repeat("quarter: ¼, micro: µ, thorn: Þ, ", 500);
|
78 |
+
$this->credis->set('utf8',$utf8str);
|
79 |
+
$this->assertEquals($utf8str, $this->credis->get('utf8'));
|
80 |
+
|
81 |
+
// Array
|
82 |
+
$this->assertTrue($this->credis->mSet(array('bar' => 'BAR', 'apple' => 'red')));
|
83 |
+
$mGet = $this->credis->mGet(array('foo','bar','empty'));
|
84 |
+
$this->assertTrue(in_array('FOO', $mGet));
|
85 |
+
$this->assertTrue(in_array('BAR', $mGet));
|
86 |
+
$this->assertTrue(in_array('', $mGet));
|
87 |
+
|
88 |
+
// Non-array
|
89 |
+
$mGet = $this->credis->mGet('foo','bar');
|
90 |
+
$this->assertTrue(in_array('FOO', $mGet));
|
91 |
+
$this->assertTrue(in_array('BAR', $mGet));
|
92 |
+
|
93 |
+
// Delete strings, null response
|
94 |
+
$this->assertEquals(2, $this->credis->del('foo','bar'));
|
95 |
+
$this->assertFalse($this->credis->get('foo'));
|
96 |
+
$this->assertFalse($this->credis->get('bar'));
|
97 |
+
|
98 |
+
// Long string
|
99 |
+
$longString = str_repeat(md5('asd'), 4096); // 128k (redis.h REDIS_INLINE_MAX_SIZE = 64k)
|
100 |
+
$this->assertTrue($this->credis->set('long', $longString));
|
101 |
+
$this->assertEquals($longString, $this->credis->get('long'));
|
102 |
+
}
|
103 |
+
|
104 |
+
public function testSets()
|
105 |
+
{
|
106 |
+
// Multiple arguments
|
107 |
+
$this->assertEquals(2, $this->credis->sAdd('myset', 'Hello', 'World'));
|
108 |
+
|
109 |
+
// Array Arguments
|
110 |
+
$this->assertEquals(1, $this->credis->sAdd('myset', array('Hello','Cruel','World')));
|
111 |
+
|
112 |
+
// Non-empty set
|
113 |
+
$members = $this->credis->sMembers('myset');
|
114 |
+
$this->assertEquals(3, count($members));
|
115 |
+
$this->assertTrue(in_array('Hello', $members));
|
116 |
+
|
117 |
+
// Empty set
|
118 |
+
$this->assertEquals(array(), $this->credis->sMembers('noexist'));
|
119 |
+
}
|
120 |
+
|
121 |
+
public function testSortedSets()
|
122 |
+
{
|
123 |
+
$this->assertEquals(1, $this->credis->zAdd('myset', 1, 'Hello'));
|
124 |
+
$this->assertEquals(1, $this->credis->zAdd('myset', 2.123, 'World'));
|
125 |
+
$this->assertEquals(1, $this->credis->zAdd('myset', 10, 'And'));
|
126 |
+
$this->assertEquals(1, $this->credis->zAdd('myset', 11, 'Goodbye'));
|
127 |
+
|
128 |
+
$this->assertEquals(4, count($this->credis->zRange('myset', 0, 4)));
|
129 |
+
$this->assertEquals(2, count($this->credis->zRange('myset', 0, 1)));
|
130 |
+
|
131 |
+
$range = $this->credis->zRange('myset', 1, 2);
|
132 |
+
$this->assertEquals(2, count($range));
|
133 |
+
$this->assertEquals('World', $range[0]);
|
134 |
+
$this->assertEquals('And', $range[1]);
|
135 |
+
|
136 |
+
$range = $this->credis->zRange('myset', 1, 2, array('withscores' => true));
|
137 |
+
$this->assertEquals(2, count($range));
|
138 |
+
$this->assertTrue(array_key_exists('World', $range));
|
139 |
+
$this->assertEquals(2.123, $range['World']);
|
140 |
+
$this->assertTrue(array_key_exists('And', $range));
|
141 |
+
$this->assertEquals(10, $range['And']);
|
142 |
+
|
143 |
+
// withscores-option is off
|
144 |
+
$range = $this->credis->zRange('myset', 0, 4, array('withscores'));
|
145 |
+
$this->assertEquals(4, count($range));
|
146 |
+
$this->assertEquals(range(0, 3), array_keys($range)); // expecting numeric array without scores
|
147 |
+
|
148 |
+
$range = $this->credis->zRange('myset', 0, 4, array('withscores' => false));
|
149 |
+
$this->assertEquals(4, count($range));
|
150 |
+
$this->assertEquals(range(0, 3), array_keys($range));
|
151 |
+
|
152 |
+
$this->assertEquals(4, count($this->credis->zRevRange('myset', 0, 4)));
|
153 |
+
$this->assertEquals(2, count($this->credis->zRevRange('myset', 0, 1)));
|
154 |
+
|
155 |
+
$range = $this->credis->zRevRange('myset', 0, 1, array('withscores' => true));
|
156 |
+
$this->assertEquals(2, count($range));
|
157 |
+
$this->assertTrue(array_key_exists('And', $range));
|
158 |
+
$this->assertEquals(10, $range['And']);
|
159 |
+
$this->assertTrue(array_key_exists('Goodbye', $range));
|
160 |
+
$this->assertEquals(11, $range['Goodbye']);
|
161 |
+
|
162 |
+
// withscores-option is off
|
163 |
+
$range = $this->credis->zRevRange('myset', 0, 4, array('withscores'));
|
164 |
+
$this->assertEquals(4, count($range));
|
165 |
+
$this->assertEquals(range(0, 3), array_keys($range)); // expecting numeric array without scores
|
166 |
+
|
167 |
+
$range = $this->credis->zRevRange('myset', 0, 4, array('withscores' => false));
|
168 |
+
$this->assertEquals(4, count($range));
|
169 |
+
$this->assertEquals(range(0, 3), array_keys($range));
|
170 |
+
|
171 |
+
$this->assertEquals(4, count($this->credis->zRangeByScore('myset', '-inf', '+inf')));
|
172 |
+
$this->assertEquals(2, count($this->credis->zRangeByScore('myset', '1', '9')));
|
173 |
+
|
174 |
+
$range = $this->credis->zRangeByScore('myset', '-inf', '+inf', array('limit' => array(1, 2)));
|
175 |
+
$this->assertEquals(2, count($range));
|
176 |
+
$this->assertEquals('World', $range[0]);
|
177 |
+
$this->assertEquals('And', $range[1]);
|
178 |
+
|
179 |
+
$range = $this->credis->zRangeByScore('myset', '-inf', '+inf', array('withscores' => true, 'limit' => array(1, 2)));
|
180 |
+
$this->assertEquals(2, count($range));
|
181 |
+
$this->assertTrue(array_key_exists('World', $range));
|
182 |
+
$this->assertEquals(2.123, $range['World']);
|
183 |
+
$this->assertTrue(array_key_exists('And', $range));
|
184 |
+
$this->assertEquals(10, $range['And']);
|
185 |
+
|
186 |
+
$range = $this->credis->zRangeByScore('myset', 10, '+inf', array('withscores' => true));
|
187 |
+
$this->assertEquals(2, count($range));
|
188 |
+
$this->assertTrue(array_key_exists('And', $range));
|
189 |
+
$this->assertEquals(10, $range['And']);
|
190 |
+
$this->assertTrue(array_key_exists('Goodbye', $range));
|
191 |
+
$this->assertEquals(11, $range['Goodbye']);
|
192 |
+
|
193 |
+
// withscores-option is off
|
194 |
+
$range = $this->credis->zRangeByScore('myset', '-inf', '+inf', array('withscores'));
|
195 |
+
$this->assertEquals(4, count($range));
|
196 |
+
$this->assertEquals(range(0, 3), array_keys($range)); // expecting numeric array without scores
|
197 |
+
|
198 |
+
$range = $this->credis->zRangeByScore('myset', '-inf', '+inf', array('withscores' => false));
|
199 |
+
$this->assertEquals(4, count($range));
|
200 |
+
$this->assertEquals(range(0, 3), array_keys($range));
|
201 |
+
|
202 |
+
$this->assertEquals(4, count($this->credis->zRevRangeByScore('myset', '+inf', '-inf')));
|
203 |
+
$this->assertEquals(2, count($this->credis->zRevRangeByScore('myset', '9', '1')));
|
204 |
+
|
205 |
+
$range = $this->credis->zRevRangeByScore('myset', '+inf', '-inf', array('limit' => array(1, 2)));
|
206 |
+
$this->assertEquals(2, count($range));
|
207 |
+
$this->assertEquals('World', $range[1]);
|
208 |
+
$this->assertEquals('And', $range[0]);
|
209 |
+
|
210 |
+
$range = $this->credis->zRevRangeByScore('myset', '+inf', '-inf', array('withscores' => true, 'limit' => array(1, 2)));
|
211 |
+
$this->assertEquals(2, count($range));
|
212 |
+
$this->assertTrue(array_key_exists('World', $range));
|
213 |
+
$this->assertEquals(2.123, $range['World']);
|
214 |
+
$this->assertTrue(array_key_exists('And', $range));
|
215 |
+
$this->assertEquals(10, $range['And']);
|
216 |
+
|
217 |
+
$range = $this->credis->zRevRangeByScore('myset', '+inf',10, array('withscores' => true));
|
218 |
+
$this->assertEquals(2, count($range));
|
219 |
+
$this->assertTrue(array_key_exists('And', $range));
|
220 |
+
$this->assertEquals(10, $range['And']);
|
221 |
+
$this->assertTrue(array_key_exists('Goodbye', $range));
|
222 |
+
$this->assertEquals(11, $range['Goodbye']);
|
223 |
+
|
224 |
+
// withscores-option is off
|
225 |
+
$range = $this->credis->zRevRangeByScore('myset', '+inf', '-inf', array('withscores'));
|
226 |
+
$this->assertEquals(4, count($range));
|
227 |
+
$this->assertEquals(range(0, 3), array_keys($range)); // expecting numeric array without scores
|
228 |
+
|
229 |
+
$range = $this->credis->zRevRangeByScore('myset', '+inf', '-inf', array('withscores' => false));
|
230 |
+
$this->assertEquals(4, count($range));
|
231 |
+
$this->assertEquals(range(0, 3), array_keys($range));
|
232 |
+
|
233 |
+
|
234 |
+
// testing zunionstore (intersection of sorted sets)
|
235 |
+
$this->credis->zAdd('myset1', 10, 'key1');
|
236 |
+
$this->credis->zAdd('myset1', 10, 'key2');
|
237 |
+
$this->credis->zAdd('myset1', 10, 'key_not_in_myset2');
|
238 |
+
|
239 |
+
$this->credis->zAdd('myset2', 15, 'key1');
|
240 |
+
$this->credis->zAdd('myset2', 15, 'key2');
|
241 |
+
$this->credis->zAdd('myset2', 15, 'key_not_in_myset1');
|
242 |
+
|
243 |
+
$this->credis->zUnionStore('myset3', array('myset1', 'myset2'));
|
244 |
+
$range = $this->credis->zRangeByScore('myset3', '-inf', '+inf', array('withscores' => true));
|
245 |
+
$this->assertEquals(4, count($range));
|
246 |
+
$this->assertTrue(array_key_exists('key1', $range));
|
247 |
+
$this->assertEquals(25, $range['key1']);
|
248 |
+
$this->assertTrue(array_key_exists('key_not_in_myset1', $range));
|
249 |
+
$this->assertEquals(15, $range['key_not_in_myset1']);
|
250 |
+
|
251 |
+
// testing zunionstore AGGREGATE option
|
252 |
+
$this->credis->zUnionStore('myset4', array('myset1', 'myset2'), array('aggregate' => 'max'));
|
253 |
+
$range = $this->credis->zRangeByScore('myset4', '-inf', '+inf', array('withscores' => true));
|
254 |
+
$this->assertEquals(4, count($range));
|
255 |
+
$this->assertTrue(array_key_exists('key1', $range));
|
256 |
+
$this->assertEquals(15, $range['key1']);
|
257 |
+
$this->assertTrue(array_key_exists('key2', $range));
|
258 |
+
$this->assertEquals(15, $range['key2']);
|
259 |
+
|
260 |
+
// testing zunionstore WEIGHTS option
|
261 |
+
$this->credis->zUnionStore('myset5', array('myset1', 'myset2'), array('weights' => array(2, 4)));
|
262 |
+
$range = $this->credis->zRangeByScore('myset5', '-inf', '+inf', array('withscores' => true));
|
263 |
+
$this->assertEquals(4, count($range));
|
264 |
+
$this->assertTrue(array_key_exists('key1', $range));
|
265 |
+
$this->assertEquals(80, $range['key1']);
|
266 |
+
}
|
267 |
+
|
268 |
+
public function testHashes()
|
269 |
+
{
|
270 |
+
$this->assertEquals(1, $this->credis->hSet('hash','field1','foo'));
|
271 |
+
$this->assertEquals(0, $this->credis->hSet('hash','field1','foo'));
|
272 |
+
$this->assertEquals('foo', $this->credis->hGet('hash','field1'));
|
273 |
+
$this->assertEquals(NULL, $this->credis->hGet('hash','x'));
|
274 |
+
$this->assertTrue($this->credis->hMSet('hash', array('field2' => 'Hello', 'field3' => 'World')));
|
275 |
+
$this->assertEquals(array('field1' => 'foo', 'field2' => 'Hello', 'nilfield' => FALSE), $this->credis->hMGet('hash', array('field1','field2','nilfield')));
|
276 |
+
$this->assertEquals(array(), $this->credis->hGetAll('nohash'));
|
277 |
+
$this->assertEquals(array('field1' => 'foo', 'field2' => 'Hello', 'field3' => 'World'), $this->credis->hGetAll('hash'));
|
278 |
+
|
279 |
+
// test integer keys
|
280 |
+
$this->assertTrue($this->credis->hMSet('hashInt', array(0 => 'Hello', 1 => 'World')));
|
281 |
+
$this->assertEquals(array(0 => 'Hello', 1 => 'World'), $this->credis->hGetAll('hashInt'));
|
282 |
+
|
283 |
+
// Test long hash values
|
284 |
+
$longString = str_repeat(md5('asd'), 4096); // 128k (redis.h REDIS_INLINE_MAX_SIZE = 64k)
|
285 |
+
$this->assertEquals(1, $this->credis->hMSet('long_hash', array('count' => 1, 'data' => $longString)), 'Set long hash value');
|
286 |
+
$this->assertEquals($longString, $this->credis->hGet('long_hash', 'data'), 'Get long hash value');
|
287 |
+
|
288 |
+
// in piplining mode
|
289 |
+
$this->assertTrue($this->credis->hMSet('hash', array('field1' => 'foo', 'field2' => 'Hello')));
|
290 |
+
|
291 |
+
$this->credis->pipeline();
|
292 |
+
$this->assertTrue($this->credis === $this->credis->hMGet('hash', array('field1','field2','nilfield')));
|
293 |
+
$this->assertEquals(array(0 => array('field1' => 'foo', 'field2' => 'Hello', 'nilfield' => FALSE)), $this->credis->exec());
|
294 |
+
|
295 |
+
$this->credis->pipeline()->multi();
|
296 |
+
$this->assertTrue($this->credis === $this->credis->hMGet('hash', array('field1','field2','nilfield')));
|
297 |
+
$this->assertEquals(array(0 => array('field1' => 'foo', 'field2' => 'Hello', 'nilfield' => FALSE)), $this->credis->exec());
|
298 |
+
}
|
299 |
+
|
300 |
+
public function testFalsey()
|
301 |
+
{
|
302 |
+
$this->assertEquals(Credis_Client::TYPE_NONE, $this->credis->type('foo'));
|
303 |
+
}
|
304 |
+
|
305 |
+
public function testPipeline()
|
306 |
+
{
|
307 |
+
$config = $this->credis->config('GET', '*');
|
308 |
+
$this->assertEquals($config, $this->credis->pipeline()->config('GET', '*')->exec()[0]);
|
309 |
+
|
310 |
+
$this->credis->pipeline();
|
311 |
+
$this->pipelineTestInternal();
|
312 |
+
$this->assertEquals(array(), $this->credis->pipeline()->exec());
|
313 |
+
}
|
314 |
+
|
315 |
+
public function testPipelineMulti()
|
316 |
+
{
|
317 |
+
$config = $this->credis->config('GET', '*');
|
318 |
+
$this->assertEquals($config, $this->credis->pipeline()->multi()->config('GET', '*')->exec()[0]);
|
319 |
+
|
320 |
+
$this->credis->pipeline()->multi();
|
321 |
+
$this->pipelineTestInternal();
|
322 |
+
$this->assertEquals(array(), $this->credis->pipeline()->multi()->exec());
|
323 |
+
}
|
324 |
+
|
325 |
+
public function testWatchMultiUnwatch()
|
326 |
+
{
|
327 |
+
$this->assertTrue($this->credis->watch('foo', 'bar'));
|
328 |
+
|
329 |
+
$reply = $this->credis->pipeline()
|
330 |
+
->multi()
|
331 |
+
->set('foo', 1)
|
332 |
+
->set('bar', 1)
|
333 |
+
->exec();
|
334 |
+
$this->assertEquals(
|
335 |
+
array(
|
336 |
+
true,
|
337 |
+
true,
|
338 |
+
), $reply
|
339 |
+
);
|
340 |
+
$this->assertTrue($this->credis->unwatch());
|
341 |
+
}
|
342 |
+
|
343 |
+
protected function pipelineTestInternal()
|
344 |
+
{
|
345 |
+
$longString = str_repeat(md5('asd') . "\r\n", 500);
|
346 |
+
$reply = $this->credis
|
347 |
+
->set('a', 123)
|
348 |
+
->get('a')
|
349 |
+
->sAdd('b', 123)
|
350 |
+
->sMembers('b')
|
351 |
+
->set('empty', '')
|
352 |
+
->get('empty')
|
353 |
+
->set('big', $longString)
|
354 |
+
->get('big')
|
355 |
+
->hset('hash', 'field1', 1)
|
356 |
+
->hset('hash', 'field2', 2)
|
357 |
+
->hgetall('hash')
|
358 |
+
->hmget('hash', array('field1', 'field3'))
|
359 |
+
->zadd('sortedSet', 1, 'member1')
|
360 |
+
->zadd('sortedSet', 2, 'member2')
|
361 |
+
->zadd('sortedSet', 3, 'member3')
|
362 |
+
->zcard('sortedSet')
|
363 |
+
->zrangebyscore('sortedSet', 1, 2)
|
364 |
+
->zrangebyscore('sortedSet', 1, 2, array('withscores' => true))
|
365 |
+
->zrevrangebyscore('sortedSet', 2, 1)
|
366 |
+
->zrevrangebyscore('sortedSet', 2, 1, array('withscores' => true))
|
367 |
+
->zrange('sortedSet', 0, 1)
|
368 |
+
->zrange('sortedSet', 0, 1, array('withscores' => true))
|
369 |
+
->zrevrange('sortedSet', 0, 1)
|
370 |
+
->zrevrange('sortedSet', 0, 1, array('withscores' => true))
|
371 |
+
->exec();
|
372 |
+
$this->assertEquals(
|
373 |
+
array(
|
374 |
+
true, // set('a', 123)
|
375 |
+
'123', // get('a')
|
376 |
+
1, // sAdd('b', 123)
|
377 |
+
array(123), // sMembers('b')
|
378 |
+
true, // set('empty', '')
|
379 |
+
'', // get('empty')
|
380 |
+
true, // set('big', $longString)
|
381 |
+
$longString, // get('big')
|
382 |
+
1, // hset('hash', 'field1', 1)
|
383 |
+
1, // hset('hash', 'field2', 2)
|
384 |
+
array( // hgetall('hash')
|
385 |
+
'field1' => 1,
|
386 |
+
'field2' => 2,
|
387 |
+
),
|
388 |
+
array( // hmget('hash', array('field1', 'field3'))
|
389 |
+
'field1' => 1,
|
390 |
+
'field3' => false,
|
391 |
+
),
|
392 |
+
1, // zadd('sortedSet', 1, 'member1')
|
393 |
+
1, // zadd('sortedSet', 2, 'member2')
|
394 |
+
1, // zadd('sortedSet', 3, 'member3')
|
395 |
+
3, // zcard('sortedSet')
|
396 |
+
array( // zrangebyscore('sortedSet', 1, 2)
|
397 |
+
'member1',
|
398 |
+
'member2',
|
399 |
+
),
|
400 |
+
array( // zrangebyscore('sortedSet', 1, 2, array('withscores' => TRUE))
|
401 |
+
'member1' => 1.0,
|
402 |
+
'member2' => 2.0,
|
403 |
+
),
|
404 |
+
array( // zrevrangebyscore('sortedSet', 1, 2)
|
405 |
+
'member2',
|
406 |
+
'member1',
|
407 |
+
),
|
408 |
+
array( // zrevrangebyscore('sortedSet', 1, 2, array('withscores' => TRUE))
|
409 |
+
'member1' => 1.0,
|
410 |
+
'member2' => 2.0,
|
411 |
+
),
|
412 |
+
array( // zrangebyscore('sortedSet', 1, 2)
|
413 |
+
'member1',
|
414 |
+
'member2',
|
415 |
+
),
|
416 |
+
array( // zrangebyscore('sortedSet', 1, 2, array('withscores' => TRUE))
|
417 |
+
'member1' => 1.0,
|
418 |
+
'member2' => 2.0,
|
419 |
+
),
|
420 |
+
array( // zrevrangebyscore('sortedSet', 1, 2)
|
421 |
+
'member3',
|
422 |
+
'member2',
|
423 |
+
),
|
424 |
+
array( // zrevrangebyscore('sortedSet', 1, 2, array('withscores' => TRUE))
|
425 |
+
'member3' => 3.0,
|
426 |
+
'member2' => 2.0,
|
427 |
+
),
|
428 |
+
), $reply
|
429 |
+
);
|
430 |
+
}
|
431 |
+
|
432 |
+
public function testTransaction()
|
433 |
+
{
|
434 |
+
$reply = $this->credis->multi()
|
435 |
+
->incr('foo')
|
436 |
+
->incr('bar')
|
437 |
+
->exec();
|
438 |
+
$this->assertEquals(array(1,1), $reply);
|
439 |
+
|
440 |
+
$reply = $this->credis->pipeline()->multi()
|
441 |
+
->incr('foo')
|
442 |
+
->incr('bar')
|
443 |
+
->exec();
|
444 |
+
$this->assertEquals(array(2,2), $reply);
|
445 |
+
|
446 |
+
$reply = $this->credis->multi()->pipeline()
|
447 |
+
->incr('foo')
|
448 |
+
->incr('bar')
|
449 |
+
->exec();
|
450 |
+
$this->assertEquals(array(3,3), $reply);
|
451 |
+
|
452 |
+
$reply = $this->credis->multi()
|
453 |
+
->set('a', 3)
|
454 |
+
->lpop('a')
|
455 |
+
->exec();
|
456 |
+
$this->assertEquals(2, count($reply));
|
457 |
+
$this->assertEquals(TRUE, $reply[0]);
|
458 |
+
$this->assertFalse($reply[1]);
|
459 |
+
}
|
460 |
+
|
461 |
+
public function testServer()
|
462 |
+
{
|
463 |
+
$this->assertArrayHasKey('used_memory', $this->credis->info());
|
464 |
+
$this->assertArrayHasKey('maxmemory', $this->credis->config('GET', 'maxmemory'));
|
465 |
+
}
|
466 |
+
|
467 |
+
public function testScripts()
|
468 |
+
{
|
469 |
+
$this->assertNull($this->credis->evalSha('1111111111111111111111111111111111111111'));
|
470 |
+
$this->assertEquals(3, $this->credis->eval('return 3'));
|
471 |
+
$this->assertEquals('09d3822de862f46d784e6a36848b4f0736dda47a', $this->credis->script('load', 'return 3'));
|
472 |
+
$this->assertEquals(3, $this->credis->evalSha('09d3822de862f46d784e6a36848b4f0736dda47a'));
|
473 |
+
|
474 |
+
$this->credis->set('foo','FOO');
|
475 |
+
$this->assertEquals('FOOBAR', $this->credis->eval("return redis.call('get', KEYS[1])..ARGV[1]", 'foo', 'BAR'));
|
476 |
+
|
477 |
+
$this->assertEquals(array(1,2,'three'), $this->credis->eval("return {1,2,'three'}"));
|
478 |
+
try {
|
479 |
+
$this->credis->eval('this-is-not-lua');
|
480 |
+
$this->fail('Expected exception on invalid script.');
|
481 |
+
} catch(CredisException $e) {
|
482 |
+
}
|
483 |
+
}
|
484 |
+
|
485 |
+
public function testPubsub()
|
486 |
+
{
|
487 |
+
if (!$this->useStandalone && version_compare(PHP_VERSION, '7.0.0') >= 0) {
|
488 |
+
$ext = new ReflectionExtension('redis');
|
489 |
+
if (version_compare($ext->getVersion(), '3.1.4RC1') < 0) {
|
490 |
+
$this->fail('phpredis 3.1.4 is required for subscribe/pSubscribe not to segfault with php 7.x');
|
491 |
+
return;
|
492 |
+
}
|
493 |
+
}
|
494 |
+
$timeout = 2;
|
495 |
+
$time = microtime(true);
|
496 |
+
$this->credis->setReadTimeout($timeout);
|
497 |
+
try {
|
498 |
+
$testCase = $this;
|
499 |
+
$this->credis->pSubscribe(array('foobar','test*'), function ($credis, $pattern, $channel, $message) use ($testCase, &$time) {
|
500 |
+
$time = time(); // Reset timeout
|
501 |
+
// Test using: redis-cli publish foobar blah
|
502 |
+
$testCase->assertEquals('blah', $message);
|
503 |
+
});
|
504 |
+
$this->fail('pSubscribe should not return.');
|
505 |
+
} catch (CredisException $e) {
|
506 |
+
$this->assertEquals($timeout, intval(microtime(true) - $time));
|
507 |
+
if ($this->useStandalone) { // phpredis does not distinguish between timed out and disconnected
|
508 |
+
$this->assertEquals($e->getCode(), CredisException::CODE_TIMED_OUT);
|
509 |
+
} else {
|
510 |
+
$this->assertEquals($e->getCode(), CredisException::CODE_DISCONNECTED);
|
511 |
+
}
|
512 |
+
}
|
513 |
+
|
514 |
+
// Perform a new subscription. Client should have either unsubscribed or disconnected
|
515 |
+
$timeout = 2;
|
516 |
+
$time = microtime(true);
|
517 |
+
$this->credis->setReadTimeout($timeout);
|
518 |
+
try {
|
519 |
+
$testCase = $this;
|
520 |
+
$this->credis->subscribe('foobar', function ($credis, $channel, $message) use ($testCase, &$time) {
|
521 |
+
$time = time(); // Reset timeout
|
522 |
+
// Test using: redis-cli publish foobar blah
|
523 |
+
$testCase->assertEquals('blah', $message);
|
524 |
+
});
|
525 |
+
$this->fail('subscribe should not return.');
|
526 |
+
} catch (CredisException $e) {
|
527 |
+
$this->assertEquals($timeout, intval(microtime(true) - $time));
|
528 |
+
if ($this->useStandalone) { // phpredis does not distinguish between timed out and disconnected
|
529 |
+
$this->assertEquals($e->getCode(), CredisException::CODE_TIMED_OUT);
|
530 |
+
} else {
|
531 |
+
$this->assertEquals($e->getCode(), CredisException::CODE_DISCONNECTED);
|
532 |
+
}
|
533 |
+
}
|
534 |
+
}
|
535 |
+
public function testDb()
|
536 |
+
{
|
537 |
+
$this->tearDown();
|
538 |
+
$this->credis = new Credis_Client($this->redisConfig[0]['host'], $this->redisConfig[0]['port'], $this->redisConfig[0]['timeout'], false, 1);
|
539 |
+
if ($this->useStandalone) {
|
540 |
+
$this->credis->forceStandalone();
|
541 |
+
}
|
542 |
+
$this->assertTrue($this->credis->set('database',1));
|
543 |
+
$this->credis->close();
|
544 |
+
$this->credis = new Credis_Client($this->redisConfig[0]['host'], $this->redisConfig[0]['port'], $this->redisConfig[0]['timeout'], false, 0);
|
545 |
+
if ($this->useStandalone) {
|
546 |
+
$this->credis->forceStandalone();
|
547 |
+
}
|
548 |
+
$this->assertFalse($this->credis->get('database'));
|
549 |
+
$this->credis = new Credis_Client($this->redisConfig[0]['host'], $this->redisConfig[0]['port'], $this->redisConfig[0]['timeout'], false, 1);
|
550 |
+
if ($this->useStandalone) {
|
551 |
+
$this->credis->forceStandalone();
|
552 |
+
}
|
553 |
+
$this->assertEquals(1,$this->credis->get('database'));
|
554 |
+
}
|
555 |
+
|
556 |
+
/**
|
557 |
+
* @group Auth
|
558 |
+
*/
|
559 |
+
public function testPassword()
|
560 |
+
{
|
561 |
+
$this->tearDown();
|
562 |
+
$this->assertArrayHasKey('password',$this->redisConfig[4]);
|
563 |
+
$this->credis = new Credis_Client($this->redisConfig[4]['host'], $this->redisConfig[4]['port'], $this->redisConfig[4]['timeout'], false, 0, $this->redisConfig[4]['password']);
|
564 |
+
if ($this->useStandalone) {
|
565 |
+
$this->credis->forceStandalone();
|
566 |
+
}
|
567 |
+
$this->assertInstanceOf('Credis_Client',$this->credis->connect());
|
568 |
+
$this->assertTrue($this->credis->set('key','value'));
|
569 |
+
$this->credis->close();
|
570 |
+
$this->credis = new Credis_Client($this->redisConfig[4]['host'], $this->redisConfig[4]['port'], $this->redisConfig[4]['timeout'], false, 0, 'wrongpassword');
|
571 |
+
if ($this->useStandalone) {
|
572 |
+
$this->credis->forceStandalone();
|
573 |
+
}
|
574 |
+
try
|
575 |
+
{
|
576 |
+
$this->credis->connect();
|
577 |
+
$this->fail('connect should fail with wrong password');
|
578 |
+
}
|
579 |
+
catch(CredisException $e)
|
580 |
+
{
|
581 |
+
$this->assertStringStartsWith('ERR invalid password', $e->getMessage());
|
582 |
+
$this->credis->close();
|
583 |
+
}
|
584 |
+
$this->credis = new Credis_Client($this->redisConfig[4]['host'], $this->redisConfig[4]['port'], $this->redisConfig[4]['timeout'], false, 0);
|
585 |
+
if ($this->useStandalone) {
|
586 |
+
$this->credis->forceStandalone();
|
587 |
+
}
|
588 |
+
try
|
589 |
+
{
|
590 |
+
$this->credis->set('key', 'value');
|
591 |
+
}
|
592 |
+
catch(CredisException $e)
|
593 |
+
{
|
594 |
+
$this->assertStringStartsWith('NOAUTH Authentication required', $e->getMessage());
|
595 |
+
|
596 |
+
}
|
597 |
+
try
|
598 |
+
{
|
599 |
+
$this->credis->auth('anotherwrongpassword');
|
600 |
+
}
|
601 |
+
catch(CredisException $e)
|
602 |
+
{
|
603 |
+
$this->assertStringStartsWith('ERR invalid password', $e->getMessage());
|
604 |
+
}
|
605 |
+
$this->assertTrue($this->credis->auth('thepassword'));
|
606 |
+
$this->assertTrue($this->credis->set('key','value'));
|
607 |
+
}
|
608 |
+
|
609 |
+
public function testGettersAndSetters()
|
610 |
+
{
|
611 |
+
$this->assertEquals($this->credis->getHost(),$this->redisConfig[0]['host']);
|
612 |
+
$this->assertEquals($this->credis->getPort(),$this->redisConfig[0]['port']);
|
613 |
+
$this->assertEquals($this->credis->getSelectedDb(),0);
|
614 |
+
$this->assertTrue($this->credis->select(2));
|
615 |
+
$this->assertEquals($this->credis->getSelectedDb(),2);
|
616 |
+
$this->assertTrue($this->credis->isConnected());
|
617 |
+
$this->credis->close();
|
618 |
+
$this->assertFalse($this->credis->isConnected());
|
619 |
+
$this->credis = new Credis_Client($this->redisConfig[0]['host'], $this->redisConfig[0]['port'], null, 'persistenceId');
|
620 |
+
if ($this->useStandalone) {
|
621 |
+
$this->credis->forceStandalone();
|
622 |
+
}
|
623 |
+
$this->assertEquals('persistenceId',$this->credis->getPersistence());
|
624 |
+
$this->credis = new Credis_Client('localhost', 12345);
|
625 |
+
if ($this->useStandalone) {
|
626 |
+
$this->credis->forceStandalone();
|
627 |
+
}
|
628 |
+
$this->credis->setMaxConnectRetries(1);
|
629 |
+
$this->setExpectedExceptionShim('CredisException','Connection to Redis localhost:12345 failed after 2 failures.');
|
630 |
+
$this->credis->connect();
|
631 |
+
}
|
632 |
+
|
633 |
+
public function testConnectionStrings()
|
634 |
+
{
|
635 |
+
$this->credis->close();
|
636 |
+
$this->credis = new Credis_Client('tcp://'.$this->redisConfig[0]['host'] . ':' . $this->redisConfig[0]['port']);
|
637 |
+
if ($this->useStandalone) {
|
638 |
+
$this->credis->forceStandalone();
|
639 |
+
}
|
640 |
+
$this->assertEquals($this->credis->getHost(),$this->redisConfig[0]['host']);
|
641 |
+
$this->assertEquals($this->credis->getPort(),$this->redisConfig[0]['port']);
|
642 |
+
$this->credis = new Credis_Client('tcp://'.$this->redisConfig[0]['host']);
|
643 |
+
if ($this->useStandalone) {
|
644 |
+
$this->credis->forceStandalone();
|
645 |
+
}
|
646 |
+
$this->assertEquals($this->credis->getPort(),$this->redisConfig[0]['port']);
|
647 |
+
$this->credis = new Credis_Client('tcp://'.$this->redisConfig[0]['host'] . ':' . $this->redisConfig[0]['port'] . '/abc123');
|
648 |
+
if ($this->useStandalone) {
|
649 |
+
$this->credis->forceStandalone();
|
650 |
+
}
|
651 |
+
$this->assertEquals('abc123',$this->credis->getPersistence());
|
652 |
+
}
|
653 |
+
|
654 |
+
public function testConnectionStringsTls()
|
655 |
+
{
|
656 |
+
$this->credis->close();
|
657 |
+
$this->credis = new Credis_Client('tls://'.$this->redisConfig[0]['host'] . ':' . $this->redisConfig[0]['port']);
|
658 |
+
if ($this->useStandalone) {
|
659 |
+
$this->credis->forceStandalone();
|
660 |
+
}
|
661 |
+
$this->assertEquals($this->credis->getHost(),$this->redisConfig[0]['host']);
|
662 |
+
$this->assertEquals($this->credis->getPort(),$this->redisConfig[0]['port']);
|
663 |
+
$this->credis = new Credis_Client('tls://'.$this->redisConfig[0]['host']);
|
664 |
+
if ($this->useStandalone) {
|
665 |
+
$this->credis->forceStandalone();
|
666 |
+
}
|
667 |
+
$this->assertEquals($this->credis->getPort(),$this->redisConfig[0]['port']);
|
668 |
+
$this->credis = new Credis_Client('tls://'.$this->redisConfig[0]['host'] . ':' . $this->redisConfig[0]['port'] . '/abc123');
|
669 |
+
if ($this->useStandalone) {
|
670 |
+
$this->credis->forceStandalone();
|
671 |
+
}
|
672 |
+
$this->assertEquals('abc123',$this->credis->getPersistence());
|
673 |
+
}
|
674 |
+
|
675 |
+
/**
|
676 |
+
* @group UnixSocket
|
677 |
+
*/
|
678 |
+
public function testConnectionStringsSocket()
|
679 |
+
{
|
680 |
+
$this->credis = new Credis_Client(realpath(__DIR__).'/redis.sock',0,null,'persistent');
|
681 |
+
if ($this->useStandalone) {
|
682 |
+
$this->credis->forceStandalone();
|
683 |
+
}
|
684 |
+
$this->credis->connect();
|
685 |
+
$this->credis->set('key','value');
|
686 |
+
$this->assertEquals('value',$this->credis->get('key'));
|
687 |
+
}
|
688 |
+
|
689 |
+
public function testInvalidTcpConnectionString()
|
690 |
+
{
|
691 |
+
$this->credis->close();
|
692 |
+
$this->setExpectedExceptionShim('CredisException','Invalid host format; expected tcp://host[:port][/persistence_identifier]');
|
693 |
+
$this->credis = new Credis_Client('tcp://'.$this->redisConfig[0]['host'] . ':abc');
|
694 |
+
if ($this->useStandalone) {
|
695 |
+
$this->credis->forceStandalone();
|
696 |
+
}
|
697 |
+
}
|
698 |
+
|
699 |
+
public function testInvalidTlsConnectionString()
|
700 |
+
{
|
701 |
+
$this->credis->close();
|
702 |
+
$this->setExpectedExceptionShim('CredisException','Invalid host format; expected tls://host[:port][/persistence_identifier]');
|
703 |
+
$this->credis = new Credis_Client('tls://'.$this->redisConfig[0]['host'] . ':abc');
|
704 |
+
if ($this->useStandalone) {
|
705 |
+
$this->credis->forceStandalone();
|
706 |
+
}
|
707 |
+
}
|
708 |
+
|
709 |
+
public function testInvalidUnixSocketConnectionString()
|
710 |
+
{
|
711 |
+
$this->credis->close();
|
712 |
+
$this->setExpectedExceptionShim('CredisException','Invalid unix socket format; expected unix:///path/to/redis.sock');
|
713 |
+
$this->credis = new Credis_Client('unix://path/to/redis.sock');
|
714 |
+
if ($this->useStandalone) {
|
715 |
+
$this->credis->forceStandalone();
|
716 |
+
}
|
717 |
+
}
|
718 |
+
|
719 |
+
public function testForceStandAloneAfterEstablishedConnection()
|
720 |
+
{
|
721 |
+
$this->credis->connect();
|
722 |
+
if ( ! $this->useStandalone) {
|
723 |
+
$this->setExpectedExceptionShim('CredisException','Cannot force Credis_Client to use standalone PHP driver after a connection has already been established.');
|
724 |
+
}
|
725 |
+
$this->credis->forceStandalone();
|
726 |
+
$this->assertTrue(true);
|
727 |
+
}
|
728 |
+
public function testHscan()
|
729 |
+
{
|
730 |
+
$this->credis->hmset('hash',array('name' => 'Jack','age' =>33));
|
731 |
+
$iterator = null;
|
732 |
+
$result = $this->credis->hscan($iterator,'hash','n*',10);
|
733 |
+
$this->assertEquals($iterator,0);
|
734 |
+
$this->assertEquals($result,['name'=>'Jack']);
|
735 |
+
}
|
736 |
+
public function testSscan()
|
737 |
+
{
|
738 |
+
$this->credis->sadd('set','name','Jack');
|
739 |
+
$this->credis->sadd('set','age','33');
|
740 |
+
$iterator = null;
|
741 |
+
$result = $this->credis->sscan($iterator,'set','n*',10);
|
742 |
+
$this->assertEquals($iterator,0);
|
743 |
+
$this->assertEquals($result,[0=>'name']);
|
744 |
+
}
|
745 |
+
public function testZscan()
|
746 |
+
{
|
747 |
+
$this->credis->zadd('sortedset',0,'name');
|
748 |
+
$this->credis->zadd('sortedset',1,'age');
|
749 |
+
$iterator = null;
|
750 |
+
$result = $this->credis->zscan($iterator,'sortedset','n*',10);
|
751 |
+
$this->assertEquals($iterator,0);
|
752 |
+
$this->assertEquals($result,['name'=>'0']);
|
753 |
+
}
|
754 |
+
public function testscan()
|
755 |
+
{
|
756 |
+
$seen = array();
|
757 |
+
for($i = 0; $i < 100; $i++)
|
758 |
+
{
|
759 |
+
$this->credis->set('name.' . $i, 'Jack');
|
760 |
+
$this->credis->set('age.' . $i, '33');
|
761 |
+
}
|
762 |
+
$iterator = null;
|
763 |
+
do
|
764 |
+
{
|
765 |
+
$result = $this->credis->scan($iterator, 'n*', 10);
|
766 |
+
if ($result === false)
|
767 |
+
{
|
768 |
+
$this->assertEquals($iterator, 0);
|
769 |
+
break;
|
770 |
+
}
|
771 |
+
else
|
772 |
+
{
|
773 |
+
foreach($result as $key)
|
774 |
+
{
|
775 |
+
$seen[$key] = true;
|
776 |
+
}
|
777 |
+
}
|
778 |
+
}
|
779 |
+
while($iterator);
|
780 |
+
$this->assertEquals(count($seen), 100);
|
781 |
+
}
|
782 |
+
|
783 |
+
public function testPing()
|
784 |
+
{
|
785 |
+
$pong = $this->credis->ping();
|
786 |
+
$this->assertEquals("PONG",$pong);
|
787 |
+
if (version_compare(phpversion('redis'), '5.0.0', '>='))
|
788 |
+
{
|
789 |
+
$pong = $this->credis->ping("test");
|
790 |
+
$this->assertEquals("test", $pong);
|
791 |
+
}
|
792 |
+
}
|
793 |
+
}
|
dependencies/vendor/colinmollenhour/credis/tests/CredisTestCommon.php
ADDED
@@ -0,0 +1,187 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
// backward compatibility (https://stackoverflow.com/a/42828632/187780)
|
3 |
+
if (!class_exists('\PHPUnit\Framework\TestCase') && class_exists('\PHPUnit_Framework_TestCase')) {
|
4 |
+
class_alias('\PHPUnit_Framework_TestCase', '\PHPUnit\Framework\TestCase');
|
5 |
+
}
|
6 |
+
|
7 |
+
class CredisTestCommon extends \PHPUnit\Framework\TestCase
|
8 |
+
{
|
9 |
+
protected $useStandalone = false;
|
10 |
+
protected $redisConfig = null;
|
11 |
+
protected $slaveConfig = null;
|
12 |
+
|
13 |
+
protected function setUp()
|
14 |
+
{
|
15 |
+
if ($this->redisConfig === null)
|
16 |
+
{
|
17 |
+
$configFile = dirname(__FILE__) . '/redis_config.json';
|
18 |
+
if (!file_exists($configFile) || !($config = file_get_contents($configFile)))
|
19 |
+
{
|
20 |
+
$this->markTestSkipped('Could not load ' . $configFile);
|
21 |
+
|
22 |
+
return;
|
23 |
+
}
|
24 |
+
$this->redisConfig = json_decode($config);
|
25 |
+
$arrayConfig = array();
|
26 |
+
foreach ($this->redisConfig as $config)
|
27 |
+
{
|
28 |
+
$arrayConfig[] = (array)$config;
|
29 |
+
}
|
30 |
+
$this->redisConfig = $arrayConfig;
|
31 |
+
}
|
32 |
+
|
33 |
+
if(!$this->useStandalone && !extension_loaded('redis')) {
|
34 |
+
$this->fail('The Redis extension is not loaded.');
|
35 |
+
}
|
36 |
+
}
|
37 |
+
|
38 |
+
/**
|
39 |
+
* Verifies the slave has connected to the master and replication has caught up
|
40 |
+
*
|
41 |
+
* @return bool
|
42 |
+
*/
|
43 |
+
protected function waitForSlaveReplication()
|
44 |
+
{
|
45 |
+
if ($this->slaveConfig === null)
|
46 |
+
{
|
47 |
+
foreach ($this->redisConfig as $config)
|
48 |
+
{
|
49 |
+
if ($config['alias'] === 'slave')
|
50 |
+
{
|
51 |
+
$this->slaveConfig = $config;
|
52 |
+
break;
|
53 |
+
}
|
54 |
+
}
|
55 |
+
if ($this->slaveConfig === null)
|
56 |
+
{
|
57 |
+
$this->markTestSkipped('Could not load slave config');
|
58 |
+
|
59 |
+
return false;
|
60 |
+
}
|
61 |
+
}
|
62 |
+
$masterConfig = new Credis_Client($this->redisConfig[0]['host'], $this->redisConfig[0]['port']);
|
63 |
+
$masterConfig->forceStandalone();
|
64 |
+
|
65 |
+
$slaveConfig = new Credis_Client($this->slaveConfig['host'], $this->slaveConfig['port']);
|
66 |
+
$slaveConfig->forceStandalone();
|
67 |
+
|
68 |
+
$start = microtime(true);
|
69 |
+
$timeout = $start + 60;
|
70 |
+
while (microtime(true) < $timeout)
|
71 |
+
{
|
72 |
+
usleep(100);
|
73 |
+
$role = $slaveConfig->role();
|
74 |
+
if ($role[0] !== 'slave')
|
75 |
+
{
|
76 |
+
$this->markTestSkipped('slave config does not points to a slave');
|
77 |
+
return false;
|
78 |
+
}
|
79 |
+
if ($role[3] === 'connected')
|
80 |
+
{
|
81 |
+
$masterRole = $masterConfig->role();
|
82 |
+
if ($masterRole[0] !== 'master')
|
83 |
+
{
|
84 |
+
$this->markTestSkipped('master config does not points to a master');
|
85 |
+
return false;
|
86 |
+
}
|
87 |
+
if ($role[4] >= $masterRole[1])
|
88 |
+
{
|
89 |
+
return true;
|
90 |
+
}
|
91 |
+
}
|
92 |
+
}
|
93 |
+
// shouldn't get here
|
94 |
+
$this->fail("Timeout (".(microtime(true) - $start)." seconds) waiting for master-slave replication to finalize");
|
95 |
+
return false;
|
96 |
+
}
|
97 |
+
|
98 |
+
public static function setUpBeforeClass()
|
99 |
+
{
|
100 |
+
if(preg_match('/^WIN/',strtoupper(PHP_OS))){
|
101 |
+
echo "Unit tests will not work automatically on Windows. Please setup all Redis instances manually:".PHP_EOL;
|
102 |
+
echo "\tredis-server redis-master.conf".PHP_EOL;
|
103 |
+
echo "\tredis-server redis-slave.conf".PHP_EOL;
|
104 |
+
echo "\tredis-server redis-2.conf".PHP_EOL;
|
105 |
+
echo "\tredis-server redis-3.conf".PHP_EOL;
|
106 |
+
echo "\tredis-server redis-4.conf".PHP_EOL;
|
107 |
+
echo "\tredis-server redis-auth.conf".PHP_EOL;
|
108 |
+
echo "\tredis-server redis-socket.conf".PHP_EOL.PHP_EOL;
|
109 |
+
} else {
|
110 |
+
chdir(__DIR__);
|
111 |
+
$directoryIterator = new DirectoryIterator(__DIR__);
|
112 |
+
foreach($directoryIterator as $item){
|
113 |
+
if(!$item->isfile() || !preg_match('/^redis\-(.+)\.conf$/',$item->getFilename()) || $item->getFilename() == 'redis-sentinel.conf'){
|
114 |
+
continue;
|
115 |
+
}
|
116 |
+
exec('redis-server '.$item->getFilename());
|
117 |
+
}
|
118 |
+
copy('redis-master.conf','redis-master.conf.bak');
|
119 |
+
copy('redis-slave.conf','redis-slave.conf.bak');
|
120 |
+
// wait for redis instances to initialize
|
121 |
+
sleep(1);
|
122 |
+
}
|
123 |
+
}
|
124 |
+
|
125 |
+
public static function tearDownAfterClass()
|
126 |
+
{
|
127 |
+
if(preg_match('/^WIN/',strtoupper(PHP_OS))){
|
128 |
+
echo "Please kill all Redis instances manually:".PHP_EOL;
|
129 |
+
} else {
|
130 |
+
chdir(__DIR__);
|
131 |
+
$directoryIterator = new DirectoryIterator(__DIR__);
|
132 |
+
foreach($directoryIterator as $item){
|
133 |
+
if(!$item->isfile() || !preg_match('/^redis\-(.+)\.pid$/',$item->getFilename())){
|
134 |
+
continue;
|
135 |
+
}
|
136 |
+
$pid = trim(file_get_contents($item->getFilename()));
|
137 |
+
if(function_exists('posix_kill')){
|
138 |
+
posix_kill($pid,15);
|
139 |
+
} else {
|
140 |
+
exec('kill '.$pid);
|
141 |
+
}
|
142 |
+
}
|
143 |
+
sleep(1); // give teardown some time to finish
|
144 |
+
@unlink('dump.rdb');
|
145 |
+
@unlink('redis-master.conf');
|
146 |
+
@unlink('redis-slave.conf');
|
147 |
+
@copy('redis-master.conf.bak','redis-master.conf');
|
148 |
+
@copy('redis-slave.conf.bak','redis-slave.conf');
|
149 |
+
}
|
150 |
+
}
|
151 |
+
|
152 |
+
|
153 |
+
//
|
154 |
+
/**
|
155 |
+
* php 7.2 compat fix, as directly polyfilling for older PHPUnit causes a function signature compatibility issue
|
156 |
+
* This is due to the defined return type
|
157 |
+
*
|
158 |
+
* Polyfill for older PHPUnit
|
159 |
+
*/
|
160 |
+
protected function createMockShim($originalClassName)
|
161 |
+
{
|
162 |
+
if (method_exists($this, 'getMock')) {
|
163 |
+
return $this->getMock($originalClassName);
|
164 |
+
} else {
|
165 |
+
return parent::createMock($originalClassName);
|
166 |
+
}
|
167 |
+
}
|
168 |
+
|
169 |
+
/**
|
170 |
+
* php 7.2 compat fix, as directly polyfilling for older PHPUnit causes a function signature compatibility issue
|
171 |
+
* This is due to the defined return type
|
172 |
+
*/
|
173 |
+
public function setExpectedExceptionShim($class, $message = NULL, $code = NULL)
|
174 |
+
{
|
175 |
+
if (method_exists($this, 'setExpectedException')) {
|
176 |
+
$this->setExpectedException($class, $message, $code);
|
177 |
+
} else {
|
178 |
+
parent::expectException($class);
|
179 |
+
if ($message !== null) {
|
180 |
+
$this->expectExceptionMessage($message);
|
181 |
+
}
|
182 |
+
if ($code !== null) {
|
183 |
+
$this->expectExceptionCode($code);
|
184 |
+
}
|
185 |
+
}
|
186 |
+
}
|
187 |
+
}
|
dependencies/vendor/colinmollenhour/credis/tests/keys.test
ADDED
@@ -0,0 +1,100 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
4a51973817aa0:4a51973817ab6
|
2 |
+
4a51973817b1b:4a51973817b29
|
3 |
+
4a51973817b43:4a51973817b50
|
4 |
+
4a51973817b61:4a51973817b6e
|
5 |
+
4a51973817b8a:4a51973817b96
|
6 |
+
4a51973817bad:4a51973817bba
|
7 |
+
4a51973817bd2:4a51973817bde
|
8 |
+
4a51973817bf6:4a51973817c01
|
9 |
+
4a51973817c19:4a51973817c24
|
10 |
+
4a51973817c3d:4a51973817c48
|
11 |
+
4a51973817c60:4a51973817c6b
|
12 |
+
4a51973817c83:4a51973817c8e
|
13 |
+
4a51973817ca7:4a51973817cb2
|
14 |
+
4a51973817cca:4a51973817cd5
|
15 |
+
4a51973817ced:4a51973817cf8
|
16 |
+
4a51973817d10:4a51973817d1c
|
17 |
+
4a51973817d33:4a51973817d3f
|
18 |
+
4a51973817d56:4a51973817d62
|
19 |
+
4a51973817d7a:4a51973817d86
|
20 |
+
4a51973817d9d:4a51973817da9
|
21 |
+
4a51973817dc1:4a51973817dcd
|
22 |
+
4a51973817de5:4a51973817df0
|
23 |
+
4a51973817e08:4a51973817e13
|
24 |
+
4a51973817e2b:4a51973817e37
|
25 |
+
4a51973817e4f:4a51973817e5a
|
26 |
+
4a51973817e72:4a51973817e7d
|
27 |
+
4a51973817e95:4a51973817ea1
|
28 |
+
4a51973817eb8:4a51973817ec4
|
29 |
+
4a51973817edb:4a51973817ee7
|
30 |
+
4a51973817eff:4a51973817f0b
|
31 |
+
4a51973817f22:4a51973817f2e
|
32 |
+
4a51973817f45:4a51973817f51
|
33 |
+
4a51973817f69:4a51973817f74
|
34 |
+
4a51973817f8c:4a51973817f98
|
35 |
+
4a51973817faf:4a51973817fbc
|
36 |
+
4a51973817fd4:4a51973817fdf
|
37 |
+
4a51973817ff7:4a51973818002
|
38 |
+
4a5197381801a:4a51973818026
|
39 |
+
4a5197381803d:4a51973818049
|
40 |
+
4a51973818060:4a5197381806c
|
41 |
+
4a51973818084:4a51973818090
|
42 |
+
4a519738180a7:4a519738180b3
|
43 |
+
4a519738180ca:4a519738180d6
|
44 |
+
4a519738180ee:4a519738180f9
|
45 |
+
4a51973818111:4a5197381811c
|
46 |
+
4a51973818134:4a5197381813f
|
47 |
+
4a51973818158:4a51973818163
|
48 |
+
4a5197381817b:4a51973818186
|
49 |
+
4a5197381819e:4a519738181a9
|
50 |
+
4a519738181c2:4a519738181cd
|
51 |
+
4a519738181e5:4a519738181f0
|
52 |
+
4a51973818208:4a51973818214
|
53 |
+
4a5197381822c:4a51973818237
|
54 |
+
4a5197381824f:4a5197381825a
|
55 |
+
4a51973818272:4a5197381827e
|
56 |
+
4a51973818296:4a519738182a1
|
57 |
+
4a519738182b9:4a519738182c4
|
58 |
+
4a519738182dc:4a519738182e8
|
59 |
+
4a519738182ff:4a5197381830b
|
60 |
+
4a51973818322:4a5197381832e
|
61 |
+
4a51973818346:4a51973818351
|
62 |
+
4a51973818369:4a51973818374
|
63 |
+
4a5197381838c:4a51973818397
|
64 |
+
4a519738183b0:4a519738183bc
|
65 |
+
4a519738183d3:4a519738183df
|
66 |
+
4a519738183f6:4a51973818403
|
67 |
+
4a5197381841b:4a51973818426
|
68 |
+
4a5197381843d:4a51973818449
|
69 |
+
4a51973818461:4a5197381846d
|
70 |
+
4a51973818484:4a51973818490
|
71 |
+
4a519738184a7:4a519738184b3
|
72 |
+
4a519738184cb:4a519738184d7
|
73 |
+
4a519738184ee:4a519738184fa
|
74 |
+
4a51973818511:4a5197381851d
|
75 |
+
4a51973818535:4a51973818540
|
76 |
+
4a51973818558:4a51973818563
|
77 |
+
4a5197381857b:4a51973818586
|
78 |
+
4a5197381859e:4a519738185aa
|
79 |
+
4a519738185c2:4a519738185ce
|
80 |
+
4a519738185e5:4a519738185f2
|
81 |
+
4a51973818609:4a51973818615
|
82 |
+
4a5197381862c:4a51973818637
|
83 |
+
4a51973818650:4a5197381865b
|
84 |
+
4a51973818673:4a5197381867e
|
85 |
+
4a51973818696:4a519738186a1
|
86 |
+
4a519738186b9:4a519738186c5
|
87 |
+
4a519738186dd:4a519738186e8
|
88 |
+
4a519738186ff:4a5197381870f
|
89 |
+
4a51973818721:4a51973818730
|
90 |
+
4a51973818747:4a51973818753
|
91 |
+
4a5197381876a:4a51973818776
|
92 |
+
4a5197381878e:4a5197381879a
|
93 |
+
4a519738187b1:4a519738187bd
|
94 |
+
4a519738187d5:4a519738187e1
|
95 |
+
4a519738187f9:4a51973818804
|
96 |
+
4a5197381881c:4a51973818827
|
97 |
+
4a51973818840:4a5197381884b
|
98 |
+
4a51973818863:4a5197381886e
|
99 |
+
4a51973818886:4a51973818892
|
100 |
+
4a519738188aa:4a519738188b5
|
dependencies/vendor/colinmollenhour/credis/tests/redis-2.conf
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
daemonize yes
|
2 |
+
port 6380
|
3 |
+
dir ./
|
4 |
+
pidfile redis-2.pid
|
5 |
+
timeout 300
|
dependencies/vendor/colinmollenhour/credis/tests/redis-3.conf
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
daemonize yes
|
2 |
+
port 6381
|
3 |
+
dir ./
|
4 |
+
pidfile redis-3.pid
|
5 |
+
timeout 300
|
dependencies/vendor/colinmollenhour/credis/tests/redis-4.conf
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
daemonize yes
|
2 |
+
port 6382
|
3 |
+
dir ./
|
4 |
+
pidfile redis-4.pid
|
5 |
+
timeout 300
|
dependencies/vendor/colinmollenhour/credis/tests/redis-auth.conf
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
daemonize yes
|
2 |
+
port 6383
|
3 |
+
dir ./
|
4 |
+
pidfile redis-auth.pid
|
5 |
+
timeout 300
|
6 |
+
requirepass "thepassword"
|
dependencies/vendor/colinmollenhour/credis/tests/redis-master.conf
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
daemonize yes
|
2 |
+
port 6379
|
3 |
+
dir ./
|
4 |
+
pidfile redis-master.pid
|
5 |
+
timeout 300
|
6 |
+
save
|
7 |
+
repl-diskless-sync yes
|
8 |
+
repl-diskless-sync-delay 0
|
dependencies/vendor/colinmollenhour/credis/tests/redis-sentinel.conf
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
daemonize yes
|
2 |
+
dir ./
|
3 |
+
pidfile redis-sentinel.pid
|
4 |
+
port 26379
|
5 |
+
sentinel monitor mymaster 127.0.0.1 6379 1
|
6 |
+
sentinel monitor masterdown 127.0.0.1 6399 1
|
dependencies/vendor/colinmollenhour/credis/tests/redis-slave.conf
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
daemonize yes
|
2 |
+
port 6385
|
3 |
+
dir ./
|
4 |
+
pidfile redis-slave.pid
|
5 |
+
timeout 300
|
6 |
+
slaveof 127.0.0.1 6379
|
7 |
+
save
|
dependencies/vendor/colinmollenhour/credis/tests/redis-socket.conf
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
daemonize yes
|
2 |
+
port 6384
|
3 |
+
dir ./
|
4 |
+
unixsocket redis.sock
|
5 |
+
unixsocketperm 755
|
6 |
+
pidfile redis-socket.pid
|
dependencies/vendor/colinmollenhour/credis/tests/redis_config.json
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
[
|
2 |
+
{"host":"127.0.0.1","port":6379,"timeout":2.5,"alias":"master","master":true},
|
3 |
+
{"host":"127.0.0.1","port":6380,"timeout":2.5,"alias":"second"},
|
4 |
+
{"host":"127.0.0.1","port":6381,"timeout":2.5,"alias":"third"},
|
5 |
+
{"host":"127.0.0.1","port":6382,"timeout":2.5,"alias":"fourth"},
|
6 |
+
{"host":"127.0.0.1","port":6383,"timeout":2.5,"alias":"auth", "password": "thepassword"},
|
7 |
+
{"host":"127.0.0.1","port":6384,"timeout":2.5,"alias":"socket"},
|
8 |
+
{"host":"127.0.0.1","port":6385,"timeout":2.5,"alias":"slave"}
|
9 |
+
]
|
dependencies/vendor/colinmollenhour/credis/tests/sentinel_config.json
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
{"host":"127.0.0.1","port":26379,"clustername":"mymaster","downclustername":"masterdown"}
|
dependencies/vendor/composer/ClassLoader.php
ADDED
@@ -0,0 +1,445 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of Composer.
|
5 |
+
*
|
6 |
+
* (c) Nils Adermann <naderman@naderman.de>
|
7 |
+
* Jordi Boggiano <j.boggiano@seld.be>
|
8 |
+
*
|
9 |
+
* For the full copyright and license information, please view the LICENSE
|
10 |
+
* file that was distributed with this source code.
|
11 |
+
*/
|
12 |
+
|
13 |
+
namespace Composer\Autoload;
|
14 |
+
|
15 |
+
/**
|
16 |
+
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
|
17 |
+
*
|
18 |
+
* $loader = new \Composer\Autoload\ClassLoader();
|
19 |
+
*
|
20 |
+
* // register classes with namespaces
|
21 |
+
* $loader->add('Symfony\Component', __DIR__.'/component');
|
22 |
+
* $loader->add('Symfony', __DIR__.'/framework');
|
23 |
+
*
|
24 |
+
* // activate the autoloader
|
25 |
+
* $loader->register();
|
26 |
+
*
|
27 |
+
* // to enable searching the include path (eg. for PEAR packages)
|
28 |
+
* $loader->setUseIncludePath(true);
|
29 |
+
*
|
30 |
+
* In this example, if you try to use a class in the Symfony\Component
|
31 |
+
* namespace or one of its children (Symfony\Component\Console for instance),
|
32 |
+
* the autoloader will first look for the class under the component/
|
33 |
+
* directory, and it will then fallback to the framework/ directory if not
|
34 |
+
* found before giving up.
|
35 |
+
*
|
36 |
+
* This class is loosely based on the Symfony UniversalClassLoader.
|
37 |
+
*
|
38 |
+
* @author Fabien Potencier <fabien@symfony.com>
|
39 |
+
* @author Jordi Boggiano <j.boggiano@seld.be>
|
40 |
+
* @see http://www.php-fig.org/psr/psr-0/
|
41 |
+
* @see http://www.php-fig.org/psr/psr-4/
|
42 |
+
*/
|
43 |
+
class ClassLoader
|
44 |
+
{
|
45 |
+
// PSR-4
|
46 |
+
private $prefixLengthsPsr4 = array();
|
47 |
+
private $prefixDirsPsr4 = array();
|
48 |
+
private $fallbackDirsPsr4 = array();
|
49 |
+
|
50 |
+
// PSR-0
|
51 |
+
private $prefixesPsr0 = array();
|
52 |
+
private $fallbackDirsPsr0 = array();
|
53 |
+
|
54 |
+
private $useIncludePath = false;
|
55 |
+
private $classMap = array();
|
56 |
+
private $classMapAuthoritative = false;
|
57 |
+
private $missingClasses = array();
|
58 |
+
private $apcuPrefix;
|
59 |
+
|
60 |
+
public function getPrefixes()
|
61 |
+
{
|
62 |
+
if (!empty($this->prefixesPsr0)) {
|
63 |
+
return call_user_func_array('array_merge', $this->prefixesPsr0);
|
64 |
+
}
|
65 |
+
|
66 |
+
return array();
|
67 |
+
}
|
68 |
+
|
69 |
+
public function getPrefixesPsr4()
|
70 |
+
{
|
71 |
+
return $this->prefixDirsPsr4;
|
72 |
+
}
|
73 |
+
|
74 |
+
public function getFallbackDirs()
|
75 |
+
{
|
76 |
+
return $this->fallbackDirsPsr0;
|
77 |
+
}
|
78 |
+
|
79 |
+
public function getFallbackDirsPsr4()
|
80 |
+
{
|
81 |
+
return $this->fallbackDirsPsr4;
|
82 |
+
}
|
83 |
+
|
84 |
+
public function getClassMap()
|
85 |
+
{
|
86 |
+
return $this->classMap;
|
87 |
+
}
|
88 |
+
|
89 |
+
/**
|
90 |
+
* @param array $classMap Class to filename map
|
91 |
+
*/
|
92 |
+
public function addClassMap(array $classMap)
|
93 |
+
{
|
94 |
+
if ($this->classMap) {
|
95 |
+
$this->classMap = array_merge($this->classMap, $classMap);
|
96 |
+
} else {
|
97 |
+
$this->classMap = $classMap;
|
98 |
+
}
|
99 |
+
}
|
100 |
+
|
101 |
+
/**
|
102 |
+
* Registers a set of PSR-0 directories for a given prefix, either
|
103 |
+
* appending or prepending to the ones previously set for this prefix.
|
104 |
+
*
|
105 |
+
* @param string $prefix The prefix
|
106 |
+
* @param array|string $paths The PSR-0 root directories
|
107 |
+
* @param bool $prepend Whether to prepend the directories
|
108 |
+
*/
|
109 |
+
public function add($prefix, $paths, $prepend = false)
|
110 |
+
{
|
111 |
+
if (!$prefix) {
|
112 |
+
if ($prepend) {
|
113 |
+
$this->fallbackDirsPsr0 = array_merge(
|
114 |
+
(array) $paths,
|
115 |
+
$this->fallbackDirsPsr0
|
116 |
+
);
|
117 |
+
} else {
|
118 |
+
$this->fallbackDirsPsr0 = array_merge(
|
119 |
+
$this->fallbackDirsPsr0,
|
120 |
+
(array) $paths
|
121 |
+
);
|
122 |
+
}
|
123 |
+
|
124 |
+
return;
|
125 |
+
}
|
126 |
+
|
127 |
+
$first = $prefix[0];
|
128 |
+
if (!isset($this->prefixesPsr0[$first][$prefix])) {
|
129 |
+
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
|
130 |
+
|
131 |
+
return;
|
132 |
+
}
|
133 |
+
if ($prepend) {
|
134 |
+
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
135 |
+
(array) $paths,
|
136 |
+
$this->prefixesPsr0[$first][$prefix]
|
137 |
+
);
|
138 |
+
} else {
|
139 |
+
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
140 |
+
$this->prefixesPsr0[$first][$prefix],
|
141 |
+
(array) $paths
|
142 |
+
);
|
143 |
+
}
|
144 |
+
}
|
145 |
+
|
146 |
+
/**
|
147 |
+
* Registers a set of PSR-4 directories for a given namespace, either
|
148 |
+
* appending or prepending to the ones previously set for this namespace.
|
149 |
+
*
|
150 |
+
* @param string $prefix The prefix/namespace, with trailing '\\'
|
151 |
+
* @param array|string $paths The PSR-4 base directories
|
152 |
+
* @param bool $prepend Whether to prepend the directories
|
153 |
+
*
|
154 |
+
* @throws \InvalidArgumentException
|
155 |
+
*/
|
156 |
+
public function addPsr4($prefix, $paths, $prepend = false)
|
157 |
+
{
|
158 |
+
if (!$prefix) {
|
159 |
+
// Register directories for the root namespace.
|
160 |
+
if ($prepend) {
|
161 |
+
$this->fallbackDirsPsr4 = array_merge(
|
162 |
+
(array) $paths,
|
163 |
+
$this->fallbackDirsPsr4
|
164 |
+
);
|
165 |
+
} else {
|
166 |
+
$this->fallbackDirsPsr4 = array_merge(
|
167 |
+
$this->fallbackDirsPsr4,
|
168 |
+
(array) $paths
|
169 |
+
);
|
170 |
+
}
|
171 |
+
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
|
172 |
+
// Register directories for a new namespace.
|
173 |
+
$length = strlen($prefix);
|
174 |
+
if ('\\' !== $prefix[$length - 1]) {
|
175 |
+
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
176 |
+
}
|
177 |
+
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
178 |
+
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
179 |
+
} elseif ($prepend) {
|
180 |
+
// Prepend directories for an already registered namespace.
|
181 |
+
$this->prefixDirsPsr4[$prefix] = array_merge(
|
182 |
+
(array) $paths,
|
183 |
+
$this->prefixDirsPsr4[$prefix]
|
184 |
+
);
|
185 |
+
} else {
|
186 |
+
// Append directories for an already registered namespace.
|
187 |
+
$this->prefixDirsPsr4[$prefix] = array_merge(
|
188 |
+
$this->prefixDirsPsr4[$prefix],
|
189 |
+
(array) $paths
|
190 |
+
);
|
191 |
+
}
|
192 |
+
}
|
193 |
+
|
194 |
+
/**
|
195 |
+
* Registers a set of PSR-0 directories for a given prefix,
|
196 |
+
* replacing any others previously set for this prefix.
|
197 |
+
*
|
198 |
+
* @param string $prefix The prefix
|
199 |
+
* @param array|string $paths The PSR-0 base directories
|
200 |
+
*/
|
201 |
+
public function set($prefix, $paths)
|
202 |
+
{
|
203 |
+
if (!$prefix) {
|
204 |
+
$this->fallbackDirsPsr0 = (array) $paths;
|
205 |
+
} else {
|
206 |
+
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
|
207 |
+
}
|
208 |
+
}
|
209 |
+
|
210 |
+
/**
|
211 |
+
* Registers a set of PSR-4 directories for a given namespace,
|
212 |
+
* replacing any others previously set for this namespace.
|
213 |
+
*
|
214 |
+
* @param string $prefix The prefix/namespace, with trailing '\\'
|
215 |
+
* @param array|string $paths The PSR-4 base directories
|
216 |
+
*
|
217 |
+
* @throws \InvalidArgumentException
|
218 |
+
*/
|
219 |
+
public function setPsr4($prefix, $paths)
|
220 |
+
{
|
221 |
+
if (!$prefix) {
|
222 |
+
$this->fallbackDirsPsr4 = (array) $paths;
|
223 |
+
} else {
|
224 |
+
$length = strlen($prefix);
|
225 |
+
if ('\\' !== $prefix[$length - 1]) {
|
226 |
+
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
227 |
+
}
|
228 |
+
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
229 |
+
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
230 |
+
}
|
231 |
+
}
|
232 |
+
|
233 |
+
/**
|
234 |
+
* Turns on searching the include path for class files.
|
235 |
+
*
|
236 |
+
* @param bool $useIncludePath
|
237 |
+
*/
|
238 |
+
public function setUseIncludePath($useIncludePath)
|
239 |
+
{
|
240 |
+
$this->useIncludePath = $useIncludePath;
|
241 |
+
}
|
242 |
+
|
243 |
+
/**
|
244 |
+
* Can be used to check if the autoloader uses the include path to check
|
245 |
+
* for classes.
|
246 |
+
*
|
247 |
+
* @return bool
|
248 |
+
*/
|
249 |
+
public function getUseIncludePath()
|
250 |
+
{
|
251 |
+
return $this->useIncludePath;
|
252 |
+
}
|
253 |
+
|
254 |
+
/**
|
255 |
+
* Turns off searching the prefix and fallback directories for classes
|
256 |
+
* that have not been registered with the class map.
|
257 |
+
*
|
258 |
+
* @param bool $classMapAuthoritative
|
259 |
+
*/
|
260 |
+
public function setClassMapAuthoritative($classMapAuthoritative)
|
261 |
+
{
|
262 |
+
$this->classMapAuthoritative = $classMapAuthoritative;
|
263 |
+
}
|
264 |
+
|
265 |
+
/**
|
266 |
+
* Should class lookup fail if not found in the current class map?
|
267 |
+
*
|
268 |
+
* @return bool
|
269 |
+
*/
|
270 |
+
public function isClassMapAuthoritative()
|
271 |
+
{
|
272 |
+
return $this->classMapAuthoritative;
|
273 |
+
}
|
274 |
+
|
275 |
+
/**
|
276 |
+
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
|
277 |
+
*
|
278 |
+
* @param string|null $apcuPrefix
|
279 |
+
*/
|
280 |
+
public function setApcuPrefix($apcuPrefix)
|
281 |
+
{
|
282 |
+
$this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null;
|
283 |
+
}
|
284 |
+
|
285 |
+
/**
|
286 |
+
* The APCu prefix in use, or null if APCu caching is not enabled.
|
287 |
+
*
|
288 |
+
* @return string|null
|
289 |
+
*/
|
290 |
+
public function getApcuPrefix()
|
291 |
+
{
|
292 |
+
return $this->apcuPrefix;
|
293 |
+
}
|
294 |
+
|
295 |
+
/**
|
296 |
+
* Registers this instance as an autoloader.
|
297 |
+
*
|
298 |
+
* @param bool $prepend Whether to prepend the autoloader or not
|
299 |
+
*/
|
300 |
+
public function register($prepend = false)
|
301 |
+
{
|
302 |
+
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
|
303 |
+
}
|
304 |
+
|
305 |
+
/**
|
306 |
+
* Unregisters this instance as an autoloader.
|
307 |
+
*/
|
308 |
+
public function unregister()
|
309 |
+
{
|
310 |
+
spl_autoload_unregister(array($this, 'loadClass'));
|
311 |
+
}
|
312 |
+
|
313 |
+
/**
|
314 |
+
* Loads the given class or interface.
|
315 |
+
*
|
316 |
+
* @param string $class The name of the class
|
317 |
+
* @return bool|null True if loaded, null otherwise
|
318 |
+
*/
|
319 |
+
public function loadClass($class)
|
320 |
+
{
|
321 |
+
if ($file = $this->findFile($class)) {
|
322 |
+
includeFile($file);
|
323 |
+
|
324 |
+
return true;
|
325 |
+
}
|
326 |
+
}
|
327 |
+
|
328 |
+
/**
|
329 |
+
* Finds the path to the file where the class is defined.
|
330 |
+
*
|
331 |
+
* @param string $class The name of the class
|
332 |
+
*
|
333 |
+
* @return string|false The path if found, false otherwise
|
334 |
+
*/
|
335 |
+
public function findFile($class)
|
336 |
+
{
|
337 |
+
// class map lookup
|
338 |
+
if (isset($this->classMap[$class])) {
|
339 |
+
return $this->classMap[$class];
|
340 |
+
}
|
341 |
+
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
|
342 |
+
return false;
|
343 |
+
}
|
344 |
+
if (null !== $this->apcuPrefix) {
|
345 |
+
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
|
346 |
+
if ($hit) {
|
347 |
+
return $file;
|
348 |
+
}
|
349 |
+
}
|
350 |
+
|
351 |
+
$file = $this->findFileWithExtension($class, '.php');
|
352 |
+
|
353 |
+
// Search for Hack files if we are running on HHVM
|
354 |
+
if (false === $file && defined('HHVM_VERSION')) {
|
355 |
+
$file = $this->findFileWithExtension($class, '.hh');
|
356 |
+
}
|
357 |
+
|
358 |
+
if (null !== $this->apcuPrefix) {
|
359 |
+
apcu_add($this->apcuPrefix.$class, $file);
|
360 |
+
}
|
361 |
+
|
362 |
+
if (false === $file) {
|
363 |
+
// Remember that this class does not exist.
|
364 |
+
$this->missingClasses[$class] = true;
|
365 |
+
}
|
366 |
+
|
367 |
+
return $file;
|
368 |
+
}
|
369 |
+
|
370 |
+
private function findFileWithExtension($class, $ext)
|
371 |
+
{
|
372 |
+
// PSR-4 lookup
|
373 |
+
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
|
374 |
+
|
375 |
+
$first = $class[0];
|
376 |
+
if (isset($this->prefixLengthsPsr4[$first])) {
|
377 |
+
$subPath = $class;
|
378 |
+
while (false !== $lastPos = strrpos($subPath, '\\')) {
|
379 |
+
$subPath = substr($subPath, 0, $lastPos);
|
380 |
+
$search = $subPath.'\\';
|
381 |
+
if (isset($this->prefixDirsPsr4[$search])) {
|
382 |
+
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
|
383 |
+
foreach ($this->prefixDirsPsr4[$search] as $dir) {
|
384 |
+
if (file_exists($file = $dir . $pathEnd)) {
|
385 |
+
return $file;
|
386 |
+
}
|
387 |
+
}
|
388 |
+
}
|
389 |
+
}
|
390 |
+
}
|
391 |
+
|
392 |
+
// PSR-4 fallback dirs
|
393 |
+
foreach ($this->fallbackDirsPsr4 as $dir) {
|
394 |
+
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
|
395 |
+
return $file;
|
396 |
+
}
|
397 |
+
}
|
398 |
+
|
399 |
+
// PSR-0 lookup
|
400 |
+
if (false !== $pos = strrpos($class, '\\')) {
|
401 |
+
// namespaced class name
|
402 |
+
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
|
403 |
+
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
|
404 |
+
} else {
|
405 |
+
// PEAR-like class name
|
406 |
+
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
|
407 |
+
}
|
408 |
+
|
409 |
+
if (isset($this->prefixesPsr0[$first])) {
|
410 |
+
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
|
411 |
+
if (0 === strpos($class, $prefix)) {
|
412 |
+
foreach ($dirs as $dir) {
|
413 |
+
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
414 |
+
return $file;
|
415 |
+
}
|
416 |
+
}
|
417 |
+
}
|
418 |
+
}
|
419 |
+
}
|
420 |
+
|
421 |
+
// PSR-0 fallback dirs
|
422 |
+
foreach ($this->fallbackDirsPsr0 as $dir) {
|
423 |
+
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
424 |
+
return $file;
|
425 |
+
}
|
426 |
+
}
|
427 |
+
|
428 |
+
// PSR-0 include paths.
|
429 |
+
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
|
430 |
+
return $file;
|
431 |
+
}
|
432 |
+
|
433 |
+
return false;
|
434 |
+
}
|
435 |
+
}
|
436 |
+
|
437 |
+
/**
|
438 |
+
* Scope isolated include.
|
439 |
+
*
|
440 |
+
* Prevents access to $this/self from included files.
|
441 |
+
*/
|
442 |
+
function includeFile($file)
|
443 |
+
{
|
444 |
+
include $file;
|
445 |
+
}
|
dependencies/vendor/composer/LICENSE
ADDED
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
2 |
+
Upstream-Name: Composer
|
3 |
+
Upstream-Contact: Jordi Boggiano <j.boggiano@seld.be>
|
4 |
+
Source: https://github.com/composer/composer
|
5 |
+
|
6 |
+
Files: *
|
7 |
+
Copyright: 2016, Nils Adermann <naderman@naderman.de>
|
8 |
+
2016, Jordi Boggiano <j.boggiano@seld.be>
|
9 |
+
License: Expat
|
10 |
+
|
11 |
+
Files: src/Composer/Util/TlsHelper.php
|
12 |
+
Copyright: 2016, Nils Adermann <naderman@naderman.de>
|
13 |
+
2016, Jordi Boggiano <j.boggiano@seld.be>
|
14 |
+
2013, Evan Coury <me@evancoury.com>
|
15 |
+
License: Expat and BSD-2-Clause
|
16 |
+
|
17 |
+
License: BSD-2-Clause
|
18 |
+
Redistribution and use in source and binary forms, with or without modification,
|
19 |
+
are permitted provided that the following conditions are met:
|
20 |
+
.
|
21 |
+
* Redistributions of source code must retain the above copyright notice,
|
22 |
+
this list of conditions and the following disclaimer.
|
23 |
+
.
|
24 |
+
* Redistributions in binary form must reproduce the above copyright notice,
|
25 |
+
this list of conditions and the following disclaimer in the documentation
|
26 |
+
and/or other materials provided with the distribution.
|
27 |
+
.
|
28 |
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
29 |
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
30 |
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
31 |
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
32 |
+
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
33 |
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
34 |
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
35 |
+
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
36 |
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
37 |
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
38 |
+
|
39 |
+
License: Expat
|
40 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
41 |
+
of this software and associated documentation files (the "Software"), to deal
|
42 |
+
in the Software without restriction, including without limitation the rights
|
43 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
44 |
+
copies of the Software, and to permit persons to whom the Software is furnished
|
45 |
+
to do so, subject to the following conditions:
|
46 |
+
.
|
47 |
+
The above copyright notice and this permission notice shall be included in all
|
48 |
+
copies or substantial portions of the Software.
|
49 |
+
.
|
50 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
51 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
52 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
53 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
54 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
55 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
56 |
+
THE SOFTWARE.
|
dependencies/vendor/composer/autoload_classmap.php
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
// autoload_classmap.php @generated by Composer
|
4 |
+
|
5 |
+
$vendorDir = dirname(dirname(__FILE__));
|
6 |
+
$baseDir = dirname($vendorDir);
|
7 |
+
|
8 |
+
return array(
|
9 |
+
'CredisException' => $vendorDir . '/colinmollenhour/credis/Client.php',
|
10 |
+
'Credis_Client' => $vendorDir . '/colinmollenhour/credis/Client.php',
|
11 |
+
'Credis_Cluster' => $vendorDir . '/colinmollenhour/credis/Cluster.php',
|
12 |
+
'Credis_Module' => $vendorDir . '/colinmollenhour/credis/Module.php',
|
13 |
+
'Credis_Sentinel' => $vendorDir . '/colinmollenhour/credis/Sentinel.php',
|
14 |
+
);
|
dependencies/vendor/composer/autoload_namespaces.php
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
// autoload_namespaces.php @generated by Composer
|
4 |
+
|
5 |
+
$vendorDir = dirname(dirname(__FILE__));
|
6 |
+
$baseDir = dirname($vendorDir);
|
7 |
+
|
8 |
+
return array(
|
9 |
+
);
|
dependencies/vendor/composer/autoload_psr4.php
ADDED
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
// autoload_psr4.php @generated by Composer
|
4 |
+
|
5 |
+
$vendorDir = dirname(dirname(__FILE__));
|
6 |
+
$baseDir = dirname($vendorDir);
|
7 |
+
|
8 |
+
return array(
|
9 |
+
'Predis\\' => array($vendorDir . '/predis/predis/src'),
|
10 |
+
);
|
dependencies/vendor/composer/autoload_real.php
ADDED
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
// autoload_real.php @generated by Composer
|
4 |
+
|
5 |
+
class ComposerAutoloaderInitf354a0cd352f791551260eb9a7611c7a
|
6 |
+
{
|
7 |
+
private static $loader;
|
8 |
+
|
9 |
+
public static function loadClassLoader($class)
|
10 |
+
{
|
11 |
+
if ('Composer\Autoload\ClassLoader' === $class) {
|
12 |
+
require __DIR__ . '/ClassLoader.php';
|
13 |
+
}
|
14 |
+
}
|
15 |
+
|
16 |
+
public static function getLoader()
|
17 |
+
{
|
18 |
+
if (null !== self::$loader) {
|
19 |
+
return self::$loader;
|
20 |
+
}
|
21 |
+
|
22 |
+
spl_autoload_register(array('ComposerAutoloaderInitf354a0cd352f791551260eb9a7611c7a', 'loadClassLoader'), true, true);
|
23 |
+
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
|
24 |
+
spl_autoload_unregister(array('ComposerAutoloaderInitf354a0cd352f791551260eb9a7611c7a', 'loadClassLoader'));
|
25 |
+
|
26 |
+
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
|
27 |
+
if ($useStaticLoader) {
|
28 |
+
require_once __DIR__ . '/autoload_static.php';
|
29 |
+
|
30 |
+
call_user_func(\Composer\Autoload\ComposerStaticInitf354a0cd352f791551260eb9a7611c7a::getInitializer($loader));
|
31 |
+
} else {
|
32 |
+
$map = require __DIR__ . '/autoload_namespaces.php';
|
33 |
+
foreach ($map as $namespace => $path) {
|
34 |
+
$loader->set($namespace, $path);
|
35 |
+
}
|
36 |
+
|
37 |
+
$map = require __DIR__ . '/autoload_psr4.php';
|
38 |
+
foreach ($map as $namespace => $path) {
|
39 |
+
$loader->setPsr4($namespace, $path);
|
40 |
+
}
|
41 |
+
|
42 |
+
$classMap = require __DIR__ . '/autoload_classmap.php';
|
43 |
+
if ($classMap) {
|
44 |
+
$loader->addClassMap($classMap);
|
45 |
+
}
|
46 |
+
}
|
47 |
+
|
48 |
+
$loader->register(true);
|
49 |
+
|
50 |
+
return $loader;
|
51 |
+
}
|
52 |
+
}
|
dependencies/vendor/composer/autoload_static.php
ADDED
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
// autoload_static.php @generated by Composer
|
4 |
+
|
5 |
+
namespace Composer\Autoload;
|
6 |
+
|
7 |
+
class ComposerStaticInitf354a0cd352f791551260eb9a7611c7a
|
8 |
+
{
|
9 |
+
public static $prefixLengthsPsr4 = array (
|
10 |
+
'P' =>
|
11 |
+
array (
|
12 |
+
'Predis\\' => 7,
|
13 |
+
),
|
14 |
+
);
|
15 |
+
|
16 |
+
public static $prefixDirsPsr4 = array (
|
17 |
+
'Predis\\' =>
|
18 |
+
array (
|
19 |
+
0 => __DIR__ . '/..' . '/predis/predis/src',
|
20 |
+
),
|
21 |
+
);
|
22 |
+
|
23 |
+
public static $classMap = array (
|
24 |
+
'CredisException' => __DIR__ . '/..' . '/colinmollenhour/credis/Client.php',
|
25 |
+
'Credis_Client' => __DIR__ . '/..' . '/colinmollenhour/credis/Client.php',
|
26 |
+
'Credis_Cluster' => __DIR__ . '/..' . '/colinmollenhour/credis/Cluster.php',
|
27 |
+
'Credis_Module' => __DIR__ . '/..' . '/colinmollenhour/credis/Module.php',
|
28 |
+
'Credis_Sentinel' => __DIR__ . '/..' . '/colinmollenhour/credis/Sentinel.php',
|
29 |
+
);
|
30 |
+
|
31 |
+
public static function getInitializer(ClassLoader $loader)
|
32 |
+
{
|
33 |
+
return \Closure::bind(function () use ($loader) {
|
34 |
+
$loader->prefixLengthsPsr4 = ComposerStaticInitf354a0cd352f791551260eb9a7611c7a::$prefixLengthsPsr4;
|
35 |
+
$loader->prefixDirsPsr4 = ComposerStaticInitf354a0cd352f791551260eb9a7611c7a::$prefixDirsPsr4;
|
36 |
+
$loader->classMap = ComposerStaticInitf354a0cd352f791551260eb9a7611c7a::$classMap;
|
37 |
+
|
38 |
+
}, null, ClassLoader::class);
|
39 |
+
}
|
40 |
+
}
|
dependencies/vendor/composer/installed.json
ADDED
@@ -0,0 +1,96 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
[
|
2 |
+
{
|
3 |
+
"name": "colinmollenhour/credis",
|
4 |
+
"version": "1.11.2",
|
5 |
+
"version_normalized": "1.11.2.0",
|
6 |
+
"source": {
|
7 |
+
"type": "git",
|
8 |
+
"url": "https://github.com/colinmollenhour/credis.git",
|
9 |
+
"reference": "b8b2bd6b87d2d4df67065f3510efb80d5f9c4e53"
|
10 |
+
},
|
11 |
+
"dist": {
|
12 |
+
"type": "zip",
|
13 |
+
"url": "https://api.github.com/repos/colinmollenhour/credis/zipball/b8b2bd6b87d2d4df67065f3510efb80d5f9c4e53",
|
14 |
+
"reference": "b8b2bd6b87d2d4df67065f3510efb80d5f9c4e53",
|
15 |
+
"shasum": ""
|
16 |
+
},
|
17 |
+
"require": {
|
18 |
+
"php": ">=5.4.0"
|
19 |
+
},
|
20 |
+
"time": "2020-06-15T19:25:47+00:00",
|
21 |
+
"type": "library",
|
22 |
+
"installation-source": "dist",
|
23 |
+
"autoload": {
|
24 |
+
"classmap": [
|
25 |
+
"Client.php",
|
26 |
+
"Cluster.php",
|
27 |
+
"Sentinel.php",
|
28 |
+
"Module.php"
|
29 |
+
]
|
30 |
+
},
|
31 |
+
"notification-url": "https://packagist.org/downloads/",
|
32 |
+
"license": [
|
33 |
+
"MIT"
|
34 |
+
],
|
35 |
+
"authors": [
|
36 |
+
{
|
37 |
+
"name": "Colin Mollenhour",
|
38 |
+
"email": "colin@mollenhour.com"
|
39 |
+
}
|
40 |
+
],
|
41 |
+
"description": "Credis is a lightweight interface to the Redis key-value store which wraps the phpredis library when available for better performance.",
|
42 |
+
"homepage": "https://github.com/colinmollenhour/credis"
|
43 |
+
},
|
44 |
+
{
|
45 |
+
"name": "predis/predis",
|
46 |
+
"version": "v1.1.1",
|
47 |
+
"version_normalized": "1.1.1.0",
|
48 |
+
"source": {
|
49 |
+
"type": "git",
|
50 |
+
"url": "https://github.com/nrk/predis.git",
|
51 |
+
"reference": "f0210e38881631afeafb56ab43405a92cafd9fd1"
|
52 |
+
},
|
53 |
+
"dist": {
|
54 |
+
"type": "zip",
|
55 |
+
"url": "https://api.github.com/repos/nrk/predis/zipball/f0210e38881631afeafb56ab43405a92cafd9fd1",
|
56 |
+
"reference": "f0210e38881631afeafb56ab43405a92cafd9fd1",
|
57 |
+
"shasum": ""
|
58 |
+
},
|
59 |
+
"require": {
|
60 |
+
"php": ">=5.3.9"
|
61 |
+
},
|
62 |
+
"require-dev": {
|
63 |
+
"phpunit/phpunit": "~4.8"
|
64 |
+
},
|
65 |
+
"suggest": {
|
66 |
+
"ext-curl": "Allows access to Webdis when paired with phpiredis",
|
67 |
+
"ext-phpiredis": "Allows faster serialization and deserialization of the Redis protocol"
|
68 |
+
},
|
69 |
+
"time": "2016-06-16T16:22:20+00:00",
|
70 |
+
"type": "library",
|
71 |
+
"installation-source": "dist",
|
72 |
+
"autoload": {
|
73 |
+
"psr-4": {
|
74 |
+
"Predis\\": "src/"
|
75 |
+
}
|
76 |
+
},
|
77 |
+
"notification-url": "https://packagist.org/downloads/",
|
78 |
+
"license": [
|
79 |
+
"MIT"
|
80 |
+
],
|
81 |
+
"authors": [
|
82 |
+
{
|
83 |
+
"name": "Daniele Alessandri",
|
84 |
+
"email": "suppakilla@gmail.com",
|
85 |
+
"homepage": "http://clorophilla.net"
|
86 |
+
}
|
87 |
+
],
|
88 |
+
"description": "Flexible and feature-complete Redis client for PHP and HHVM",
|
89 |
+
"homepage": "http://github.com/nrk/predis",
|
90 |
+
"keywords": [
|
91 |
+
"nosql",
|
92 |
+
"predis",
|
93 |
+
"redis"
|
94 |
+
]
|
95 |
+
}
|
96 |
+
]
|
dependencies/vendor/predis/predis/CHANGELOG.md
ADDED
@@ -0,0 +1,985 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
v1.1.1 (2016-06-16)
|
2 |
+
================================================================================
|
3 |
+
|
4 |
+
- __FIX__: `password` and `database` from the global `parameters` client option
|
5 |
+
were still being applied to sentinels connections making them fail (sentinels
|
6 |
+
do not understand the `AUTH` and `SELECT` commands) (PR #346).
|
7 |
+
|
8 |
+
- __FIX__: when a sentinel instance reports no sentinel for a service, invoking
|
9 |
+
`connect()` on the redis-sentinel connection backend should fall back to the
|
10 |
+
master connection instead of failing (ISSUE #342).
|
11 |
+
|
12 |
+
- __FIX__: the two connection backends based on ext-phpiredis has some kind of
|
13 |
+
issues with the GC and the internal use of closures as reader callbacks that
|
14 |
+
prevented connections going out of scope from being properly collected and the
|
15 |
+
underlying stream or socket resources from being closed and freed. This should
|
16 |
+
not have had any actual effect in real-world scenarios due to the lifecycle of
|
17 |
+
PHP scripts, but we fixed it anyway (ISSUE #345).
|
18 |
+
|
19 |
+
|
20 |
+
v1.1.0 (2016-06-02)
|
21 |
+
================================================================================
|
22 |
+
|
23 |
+
- The default server profile for the client now targets Redis 3.2.
|
24 |
+
|
25 |
+
- Responses to the following commands are not casted into booleans anymore, the
|
26 |
+
original integer value is returned: `SETNX`, `MSETNX`, `SMOVE`, `SISMEMBER`,
|
27 |
+
`HSET`, `HSETNX`, `HEXISTS`, `PFADD`, `EXISTS`, `MOVE`, `PERSIST`, `EXPIRE`,
|
28 |
+
`EXPIREAT`, `RENAMENX`. This change does not have a significant impact unless
|
29 |
+
when using strict comparisons (=== and !==) the returned value.
|
30 |
+
|
31 |
+
- Non-boolean string values passed to the `persistent` connection parameter can
|
32 |
+
be used to create different persistent connections. Note that this feature was
|
33 |
+
already present in Predis but required both `persistent` and `path` to be set
|
34 |
+
as illustrated by [#139](https://github.com/nrk/predis/pull/139). This change
|
35 |
+
is needed to prevent confusion with how `path` is used to select a database
|
36 |
+
when using the `redis` scheme.
|
37 |
+
|
38 |
+
- The client throws exceptions when Redis returns any kind of error response to
|
39 |
+
initialization commands (the ones being automatically sent when a connection
|
40 |
+
is established, such as `SELECT` and `AUTH` when database and password are set
|
41 |
+
in connection parameters) regardless of the value of the exception option.
|
42 |
+
|
43 |
+
- Using `unix:///path/to/socket` in URI strings to specify a UNIX domain socket
|
44 |
+
file is now deprecated in favor of the format `unix:/path/to/socket` (note the
|
45 |
+
lack of the double slash after the scheme) and will not be supported starting
|
46 |
+
with the next major release.
|
47 |
+
|
48 |
+
- Implemented full support for redis-sentinel.
|
49 |
+
|
50 |
+
- Implemented the ability to specify default connection parameters for aggregate
|
51 |
+
connections with the new `parameters` client option. These parameters augment
|
52 |
+
the usual user-supplied connection parameters (but do not take the precedence
|
53 |
+
over them) when creating new connections and they are mostly useful when the
|
54 |
+
client is using aggregate connections such as redis-cluster and redis-sentinel
|
55 |
+
as these backends can create new connections on the fly based on responses and
|
56 |
+
redirections from Redis.
|
57 |
+
|
58 |
+
- Redis servers protected by SSL-encrypted connections can be accessed by using
|
59 |
+
the `tls` or `rediss` scheme in connection parameters along with SSL-specific
|
60 |
+
options in the `ssl` parameter (see http://php.net/manual/context.ssl.php).
|
61 |
+
|
62 |
+
- `Predis\Client` implements `IteratorAggregate` making it possible to iterate
|
63 |
+
over traversable aggregate connections and get a new client instance for each
|
64 |
+
Redis node.
|
65 |
+
|
66 |
+
- Iterating over an instance of `Predis\Connection\Aggregate\RedisCluster` will
|
67 |
+
return all the connections mapped in the slots map instead of just the ones in
|
68 |
+
the pool. This change makes it possible, when the slots map is retrieved from
|
69 |
+
Redis, to iterate over all of the master nodes in the cluster. When the use of
|
70 |
+
`CLUSTER SLOTS` is disabled via the `useClusterSlots()` method, the iteration
|
71 |
+
returns only the connections with slots ranges associated in their parameters
|
72 |
+
or the ones initialized by `-MOVED` responses in order to make the behaviour
|
73 |
+
of the iteration consistent between the two modes of operation.
|
74 |
+
|
75 |
+
- Various improvements to `Predis\Connection\Aggregate\MasterSlaveReplication`
|
76 |
+
(the "basic" replication backend, not the new one based on redis-sentinel):
|
77 |
+
|
78 |
+
- When the client is not able to send a read-only command to a slave because
|
79 |
+
the current connection fails or the slave is resyncing (`-LOADING` response
|
80 |
+
returned by Redis), the backend discards the failed connection and performs
|
81 |
+
a new attempt on the next slave. When no other slave is available the master
|
82 |
+
server is used for read-only commands as last resort.
|
83 |
+
|
84 |
+
- It is possible to discover the current replication configuration on the fly
|
85 |
+
by invoking the `discover()` method which internally relies on the output of
|
86 |
+
the command `INFO REPLICATION` executed against the master server or one of
|
87 |
+
the slaves. The backend can also be configured to do this automatically when
|
88 |
+
it fails to reach one of the servers.
|
89 |
+
|
90 |
+
- Implemented the `switchToMaster()` and `switchToSlave()` methods to make it
|
91 |
+
easier to force a switch to the master server or a random slave when needed.
|
92 |
+
|
93 |
+
|
94 |
+
v1.0.4 (2016-05-30)
|
95 |
+
================================================================================
|
96 |
+
|
97 |
+
- Added new profile for Redis 3.2 with its new commands: `HSTRLEN`, `BITFIELD`,
|
98 |
+
`GEOADD`, `GEOHASH`, `GEOPOS`, `GEODIST`, `GEORADIUS`, `GEORADIUSBYMEMBER`.
|
99 |
+
The default server profile for Predis is still the one for Redis 3.0 you must
|
100 |
+
set the `profile` client option to `3.2` when initializing the client in order
|
101 |
+
to be able to use them when connecting to Redis 3.2.
|
102 |
+
|
103 |
+
- Various improvements in the handling of redis-cluster:
|
104 |
+
|
105 |
+
- If the connection to a specific node fails when executing a command, the
|
106 |
+
client tries to connect to another node in order to refresh the slots map
|
107 |
+
and perform a new attempt to execute the command.
|
108 |
+
|
109 |
+
- Connections to nodes can be preassigned to non-contiguous slot ranges via
|
110 |
+
the `slots` parameter using a comma separator. This is how it looks like
|
111 |
+
in practice: `tcp://127.0.0.1:6379?slots=0-5460,5500-5600,11000`.
|
112 |
+
|
113 |
+
- __FIX__: broken values returned by `Predis\Collection\Iterator\HashKey` when
|
114 |
+
iterating hash keys containing integer fields (PR #330, ISSUE #331).
|
115 |
+
|
116 |
+
- __FIX__: prevent failures when `Predis\Connection\StreamConnection` serializes
|
117 |
+
commands with holes in their arguments (e.g. `[0 => 'key:0', 2 => 'key:2']`).
|
118 |
+
The same fix has been applied to `Predis\Protocol\Text\RequestSerializer`.
|
119 |
+
(ISSUE #316).
|
120 |
+
|
121 |
+
|
122 |
+
v1.0.3 (2015-07-30)
|
123 |
+
================================================================================
|
124 |
+
|
125 |
+
- __FIX__: the previous release introduced a severe regression on HHVM that made
|
126 |
+
the library unable to connect to Redis when using IPv4 addresses. Code running
|
127 |
+
on the standard PHP interpreter is not affected.
|
128 |
+
|
129 |
+
|
130 |
+
v1.0.2 (2015-07-30)
|
131 |
+
================================================================================
|
132 |
+
|
133 |
+
- IPv6 is now fully supported.
|
134 |
+
|
135 |
+
- Added `redis` as an accepted scheme for connection parameters. When using this
|
136 |
+
scheme, the rules used to parse URI strings match the provisional registration
|
137 |
+
[published by IANA](http://www.iana.org/assignments/uri-schemes/prov/redis).
|
138 |
+
|
139 |
+
- Added new or missing commands: `HSTRLEN` (>= 3.2), `ZREVRANGEBYLEX` (>= 2.8)
|
140 |
+
and `MIGRATE` (>= 2.6).
|
141 |
+
|
142 |
+
- Implemented support for the `ZADD` modifiers `NX|XX`, `CH`, `INCR` (Redis >=
|
143 |
+
3.0.2) using the simplified signature where scores and members are passed as
|
144 |
+
a named array.
|
145 |
+
|
146 |
+
- __FIX__: `Predis\Configuration\Options` must not trigger the autoloader when
|
147 |
+
option values are strings (ISSUE #257).
|
148 |
+
|
149 |
+
- __FIX__: `BITPOS` was not defined in the key-prefix processor (ISSUE #265) and
|
150 |
+
in the replication strategy.
|
151 |
+
|
152 |
+
|
153 |
+
v1.0.1 (2015-01-02)
|
154 |
+
================================================================================
|
155 |
+
|
156 |
+
- Added `BITPOS` to the server profile for Redis 2.8.
|
157 |
+
|
158 |
+
- Connection timeout for read/write operations can now be set for UNIX sockets
|
159 |
+
where the underlying connection uses PHP's stream.
|
160 |
+
|
161 |
+
- __FIX__: broken values returned by `Predis\Collection\Iterator\SortedSetKey`
|
162 |
+
when iterating sorted set containing integer members (ISSUE #216).
|
163 |
+
|
164 |
+
- __FIX__: applied a minor workaround for a bug in old versions of PHP < 5.3.9
|
165 |
+
affecting inheritance.
|
166 |
+
|
167 |
+
- __FIX__: prevent E_NOTICE warnings when using INFO [section] returns an empty
|
168 |
+
response due to an unsupported specific set of information requested to Redis.
|
169 |
+
|
170 |
+
|
171 |
+
v1.0.0 (2014-08-01)
|
172 |
+
================================================================================
|
173 |
+
|
174 |
+
- Switched to PSR-4 for autoloading.
|
175 |
+
|
176 |
+
- The default server profile for Redis is `3.0`.
|
177 |
+
|
178 |
+
- Removed server profile for Redis 1.2.
|
179 |
+
|
180 |
+
- Added `SENTINEL` to the profile for Redis 2.6 and `PUBSUB` to the profile for
|
181 |
+
Redis 2.8.
|
182 |
+
|
183 |
+
- `Predis\Client` can now send raw commands using `Predis\Client::executeRaw()`.
|
184 |
+
|
185 |
+
- Status responses are returned as instances of `Predis\Response\Status`, for
|
186 |
+
example +OK is not returned as boolean TRUE anymore which is a breaking change
|
187 |
+
for those using strict comparisons. Status responses can be casted to string
|
188 |
+
values carrying the original payload, so one can do `$response == 'OK'` which
|
189 |
+
is also more akin to how Redis replies to clients.
|
190 |
+
|
191 |
+
- Commands `ZRANGE`, `ZRANGEBYSCORE`, `ZREVRANGE` and `ZREVRANGEBYSCORE` using
|
192 |
+
`WITHSCORE` return a named array of member => score instead of using an array
|
193 |
+
of [member, score] elements. Insertion order is preserved anyway due to how
|
194 |
+
PHP works internally.
|
195 |
+
|
196 |
+
- The command `ZSCAN` returns a named array of member => score instead of using
|
197 |
+
an array of [member, score] elements. Insertion order is preserved anyway due
|
198 |
+
to how PHP works internally.
|
199 |
+
|
200 |
+
- The rules for redis-cluster are now leveraged for empty key tags when using
|
201 |
+
client-side sharding, which means that when one or the first occurrence of {}
|
202 |
+
is found in a key it will most likely produce a different hash than previous
|
203 |
+
versions of Predis thus leading to a different partitioning in these cases.
|
204 |
+
|
205 |
+
- Invoking `Predis\Client::connect()` when the underlying connection has been
|
206 |
+
already established does not throw any exception anymore, now the connection
|
207 |
+
simply does not attempt to perform any operation.
|
208 |
+
|
209 |
+
- Added the `aggregate` client option, useful to fully customize how the client
|
210 |
+
should aggregate multiple connections when an array of connection parameters
|
211 |
+
is passed to `Predis\Client::__construct()`.
|
212 |
+
|
213 |
+
- Dropped support for streamable multibulk responses. Actually we still ship the
|
214 |
+
iterator response classes just in case anyone would want to build custom stuff
|
215 |
+
at a level lower than the client abstraction (our standard and composable text
|
216 |
+
protocol processors still handle them and can be used as an example).
|
217 |
+
|
218 |
+
- Simplified the implementation of connection parameters by removing method used
|
219 |
+
to cast to int / bool / float certain parameters supplied by users. Casting
|
220 |
+
values, if deemed necessary, should be done by the consumer or you can just
|
221 |
+
subclass `Predis\Connection\Parameters` and override the `filter()` method.
|
222 |
+
|
223 |
+
- Changed a couple of options for our transaction abstraction:
|
224 |
+
|
225 |
+
- `exceptions`: overrides the value of the client option with the same name.
|
226 |
+
Please note that it does not affect all the transaction control commands
|
227 |
+
such as `MULTI`, `EXEC`, `DISCARD`, `WATCH` and `UNWATCH`.
|
228 |
+
- `on_retry`: this option has been removed.
|
229 |
+
|
230 |
+
- Removed pipeline executors, now command pipelines can be easily customized by
|
231 |
+
extending the standard `Predis\Pipeline\Pipeline` class. Accepted options when
|
232 |
+
creating a pipeline using `Predis\Client::pipeline()` are:
|
233 |
+
|
234 |
+
- `atomic`: returns a pipeline wrapped in a MULTI / EXEC transaction
|
235 |
+
(class: `Predis\Pipeline\Atomic`).
|
236 |
+
- `fire-and-forget`: returns a pipeline that does not read back responses
|
237 |
+
(class: `Predis\Pipeline\FireAndForget`).
|
238 |
+
|
239 |
+
- Renamed the two base abstract command classes:
|
240 |
+
|
241 |
+
- `Predis\Command\AbstractCommand` is now `Predis\Command\Command`
|
242 |
+
- `Predis\Command\ScriptedCommand` is now `Predis\Command\ScriptCommand`
|
243 |
+
|
244 |
+
- Dropped `Predis\Command\Command::__toString()` (see issue #151).
|
245 |
+
|
246 |
+
- The key prefixing logic has been moved from command classes to the key prefix
|
247 |
+
processor. Developers can define or override handlers used to prefix keys, but
|
248 |
+
they can also define the needed logic in their command classes by implementing
|
249 |
+
`Predis\Command\PrefixableCommandInterface` just like before.
|
250 |
+
|
251 |
+
- `Predis\PubSub\DispatcherLoop` now takes a `Predis\PubSub\Consumer` instance
|
252 |
+
as the sole argument of its constructor instead of `Predis\ClientInterface`.
|
253 |
+
|
254 |
+
- All of the interfaces and classes related to translated Redis response types
|
255 |
+
have been moved in the new `Predis\Response` namespace and most of them have
|
256 |
+
been renamed to make their fully-qualified name less redundant. Now the base
|
257 |
+
response interface is `Predis\Response\ResponseInterface`.
|
258 |
+
|
259 |
+
- Renamed interface `Predis\Command\Processor\CommandProcessorInterface` to a
|
260 |
+
shorter `Predis\Command\Processor\ProcessorInterface`. Also removed interface
|
261 |
+
for chain processors since it is basically useless.
|
262 |
+
|
263 |
+
- Renamed `Predis\ExecutableContextInterface` to `Predis\ClientContextInterface`
|
264 |
+
and augmented it with a couple of required methods since this interface is no
|
265 |
+
more comparable to a basic client as it could be misleading.
|
266 |
+
|
267 |
+
- The `Predis\Option` namespace is now known as `Predis\Configuration` and have
|
268 |
+
a fully-reworked `Options` class with the ability to lazily initialize values
|
269 |
+
using objects that responds to `__invoke()` (not all the kinds of callables)
|
270 |
+
even for custom options defined by the user.
|
271 |
+
|
272 |
+
- Renamed `Predis\Connection\ConnectionInterface::writeCommand()` into
|
273 |
+
`writeRequest()` for consistency with its counterpart, `readResponse()`.
|
274 |
+
|
275 |
+
- Renamed `Predis\Connection\SingleConnectionInterface::pushInitCommand()` into
|
276 |
+
`addConnectCommand()` which is more obvious.
|
277 |
+
|
278 |
+
- Renamed the connection class based on both ext-phpiredis and ext-socket into
|
279 |
+
`Predis\Connection\PhpiredisSocketConnection`. The one based on PHP's streams
|
280 |
+
is still named `Predis\Connection\PhpiredisStreamConnection`.
|
281 |
+
|
282 |
+
- Renamed the connection factory class to `Predis\Connection\Factory`. Now its
|
283 |
+
constructor does not require anymore a profile instance to create `AUTH` and
|
284 |
+
`SELECT` commands when parameters contain both `password` and `database`. Raw
|
285 |
+
commands will be used instead.
|
286 |
+
|
287 |
+
- Renamed the connection parameters class to `Predis\Connection\Parameters`. Now
|
288 |
+
its constructor accepts only named arrays, but instances can still be created
|
289 |
+
using both URIs or arrays using the static method `Parameters::create()`.
|
290 |
+
|
291 |
+
- The profile factory code has been extracted from the abstract Redis profile
|
292 |
+
class and now lives in `Predis\Profile\Factory`.
|
293 |
+
|
294 |
+
- The `Predis\Connection` namespace has been completely reorganized by renaming
|
295 |
+
a few classes and interfaces and adding some sub-namespaces.
|
296 |
+
|
297 |
+
- Most classes and interfaces in the `Predis\Protocol` namespace have been moved
|
298 |
+
or renamed while rationalizing the whole API for external protocol processors.
|
299 |
+
|
300 |
+
|
301 |
+
v0.8.7 (2014-08-01)
|
302 |
+
================================================================================
|
303 |
+
|
304 |
+
- Added `3.0` in the server profiles aliases list for Redis 3.0. `2.8` is still
|
305 |
+
the default server profile and `dev` still targets Redis 3.0.
|
306 |
+
|
307 |
+
- Added `COMMAND` to the server profile for Redis 2.8.
|
308 |
+
|
309 |
+
- Switched internally to the `CLUSTER SLOTS` command instead of `CLUSTER NODES`
|
310 |
+
to fetch the updated slots map from redis-cluster. This change requires users
|
311 |
+
to upgrade Redis nodes to >= 3.0.0b7.
|
312 |
+
|
313 |
+
- The updated slots map is now fetched automatically from redis-cluster upon the
|
314 |
+
first `-MOVED` response by default. This change makes it possible to feed the
|
315 |
+
client constructor with only a few nodes of the actual cluster composition,
|
316 |
+
without needing a more complex configuration.
|
317 |
+
|
318 |
+
- Implemented support for `PING` in PUB/SUB loop for Redis >= 3.0.0b8.
|
319 |
+
|
320 |
+
- The default client-side sharding strategy and the one for redis-cluster now
|
321 |
+
share the same implementations as they follow the same rules. One difference,
|
322 |
+
aside from the different hashing function used to calculate distribution, is
|
323 |
+
in how empty hash tags like {} are treated by redis-cluster.
|
324 |
+
|
325 |
+
- __FIX__: the patch applied to fix #180 introduced a regression affecting read/
|
326 |
+
write timeouts in `Predis\Connection\PhpiredisStreamConnection`. Unfortunately
|
327 |
+
the only possible solution requires PHP 5.4+. On PHP 5.3, read/write timeouts
|
328 |
+
will be ignored from now on.
|
329 |
+
|
330 |
+
|
331 |
+
v0.8.6 (2014-07-15)
|
332 |
+
================================================================================
|
333 |
+
|
334 |
+
- Redis 2.8 is now the default server profile as there are no changes that would
|
335 |
+
break compatibility with previous releases.
|
336 |
+
|
337 |
+
- Added `PFADD`, `PFCOUNT`, `PFMERGE` to the server profile for Redis 2.8 for
|
338 |
+
handling the HyperLogLog data structure introduced in Redis 2.8.9.
|
339 |
+
|
340 |
+
- Added `ZLEXCOUNT`, `ZRANGEBYLEX`, `ZREMRANGEBYLEX` to the server profile for
|
341 |
+
Redis 2.8 for handling lexicographic operations on members of sorted sets.
|
342 |
+
|
343 |
+
- Added support for key hash tags when using redis-cluster (Redis 3.0.0b1).
|
344 |
+
|
345 |
+
- __FIX__: minor tweaks to make Predis compatible with HHVM >= 2.4.0.
|
346 |
+
|
347 |
+
- __FIX__: responses to `INFO` are now properly parsed and will not break when
|
348 |
+
redis sentinel is being used (ISSUE #154).
|
349 |
+
|
350 |
+
- __FIX__: added missing support for `INCRBYFLOAT` in cluster and replication
|
351 |
+
configurations (ISSUE #159).
|
352 |
+
|
353 |
+
- __FIX__: fix parsing of the output of `CLUSTER NODES` to fetch the slots map
|
354 |
+
from a node when redis-cluster has slaves in its configuration (ISSUE #165).
|
355 |
+
|
356 |
+
- __FIX__: prevent a stack overflow when iterating over large Redis collections
|
357 |
+
using our abstraction for cursor-based iterators (ISSUE #182).
|
358 |
+
|
359 |
+
- __FIX__: properly discards transactions when the server immediately returns an
|
360 |
+
error response (e.g. -OOM or -ERR on invalid arguments for a command) instead
|
361 |
+
of a +QUEUED response (ISSUE #187).
|
362 |
+
|
363 |
+
- Upgraded to PHPUnit 4.* for the test suite.
|
364 |
+
|
365 |
+
|
366 |
+
v0.8.5 (2014-01-16)
|
367 |
+
================================================================================
|
368 |
+
|
369 |
+
- Added `2.8` in the server profiles aliases list for Redis 2.8. `2.6` is still
|
370 |
+
the default server profile and `dev` now targets Redis 3.0.
|
371 |
+
|
372 |
+
- Added `SCAN`, `SSCAN`, `ZSCAN`, `HSCAN` to the server profile for Redis 2.8.
|
373 |
+
|
374 |
+
- Implemented PHP iterators for incremental iterations over Redis collections:
|
375 |
+
|
376 |
+
- keyspace (cursor-based iterator using `SCAN`)
|
377 |
+
- sets (cursor-based iterator using `SSCAN`)
|
378 |
+
- sorted sets (cursor-based iterator using `ZSCAN`)
|
379 |
+
- hashes (cursor-based iterator using `HSCAN`)
|
380 |
+
- lists (plain iterator using `LRANGE`)
|
381 |
+
|
382 |
+
- It is now possible to execute "raw commands" using `Predis\Command\RawCommand`
|
383 |
+
and a variable list of command arguments. Input arguments are not filtered and
|
384 |
+
responses are not parsed, which means arguments must follow the signature of
|
385 |
+
the command as defined by Redis and complex responses are left untouched.
|
386 |
+
|
387 |
+
- URI parsing for connection parameters has been improved and has slightly less
|
388 |
+
overhead when the number of fields in the querystring grows. New features are:
|
389 |
+
|
390 |
+
- Parsing does not break when value of a field contains one or more "=".
|
391 |
+
- Repeated fieldnames using [] produce an array of values.
|
392 |
+
- Empty or incomplete "key=value" pairs result in an empty string for "key".
|
393 |
+
|
394 |
+
- Various improvements and fixes to the redis-cluster connection backend:
|
395 |
+
|
396 |
+
- __FIX__: the `ASKING` command is sent upon -ASK redirections.
|
397 |
+
- An updated slots-map can be fetched from nodes using the `CLUSTER NODES`
|
398 |
+
command. By default this is a manual operation but can be enabled to get
|
399 |
+
automatically done upon -MOVED redirections.
|
400 |
+
- It is possible to specify a common set of connection parameters that are
|
401 |
+
applied to connections created on the fly upon redirections to nodes not
|
402 |
+
part of the initial pool.
|
403 |
+
|
404 |
+
- List of deprecated methods:
|
405 |
+
|
406 |
+
- `Predis\Client::multiExec()`: superseded by `Predis\Client::transaction()`
|
407 |
+
and to be removed in the next major release.
|
408 |
+
- `Predis\Client::pubSub()`: superseded by `Predis\Client::pubSubLoop()` and
|
409 |
+
to be removed in the next major release. This change was needed due to the
|
410 |
+
recently introduced `PUBSUB` command in Redis 2.8.
|
411 |
+
|
412 |
+
|
413 |
+
v0.8.4 (2013-07-27)
|
414 |
+
================================================================================
|
415 |
+
|
416 |
+
- Added `DUMP` and `RESTORE` to the server profile for Redis 2.6.
|
417 |
+
|
418 |
+
- Connection exceptions now report basic host details in their messages.
|
419 |
+
|
420 |
+
- Allow `Predis\Connection\PhpiredisConnection` to use a random IP when a host
|
421 |
+
actually has several IPs (ISSUE #116).
|
422 |
+
|
423 |
+
- __FIX__: allow `HMSET` when using a cluster of Redis nodes with client-side
|
424 |
+
sharding or redis-cluster (ISSUE #106).
|
425 |
+
|
426 |
+
- __FIX__: set `WITHSCORES` modifer for `ZRANGE`, `ZREVRANGE`, `ZRANGEBYSCORE`
|
427 |
+
and `ZREVRANGEBYSCORE` only when the options array passed to these commands
|
428 |
+
has `WITHSCORES` set to `true` (ISSUE #107).
|
429 |
+
|
430 |
+
- __FIX__: scripted commands falling back from `EVALSHA` to `EVAL` resulted in
|
431 |
+
PHP errors when using a prefixed client (ISSUE #109).
|
432 |
+
|
433 |
+
- __FIX__: `Predis\PubSub\DispatcherLoop` now works properly when using key
|
434 |
+
prefixing (ISSUE #114).
|
435 |
+
|
436 |
+
|
437 |
+
v0.8.3 (2013-02-18)
|
438 |
+
================================================================================
|
439 |
+
|
440 |
+
- Added `CLIENT SETNAME` and `CLIENT GETNAME` (ISSUE #102).
|
441 |
+
|
442 |
+
- Implemented the `Predis\Connection\PhpiredisStreamConnection` class using the
|
443 |
+
`phpiredis` extension like `Predis\Connection\PhpiredisStreamConnection`, but
|
444 |
+
without requiring the `socket` extension since it relies on PHP's streams.
|
445 |
+
|
446 |
+
- Added support for the TCP_NODELAY flag via the `tcp_nodelay` parameter for
|
447 |
+
stream-based connections, namely `Predis\Connection\StreamConnection` and
|
448 |
+
`Predis\Connection\PhpiredisStreamConnection` (requires PHP >= 5.4.0).
|
449 |
+
|
450 |
+
- Updated the aggregated connection class for redis-cluster to work with 16384
|
451 |
+
hash slots instead of 4096 to reflect the recent change from redis unstable
|
452 |
+
([see this commit](https://github.com/antirez/redis/commit/ebd666d)).
|
453 |
+
|
454 |
+
- The constructor of `Predis\Client` now accepts a callable as first argument
|
455 |
+
returning `Predis\Connection\ConnectionInterface`. Users can create their
|
456 |
+
own self-contained strategies to create and set up the underlying connection.
|
457 |
+
|
458 |
+
- Users should return `0` from `Predis\Command\ScriptedCommand::getKeysCount()`
|
459 |
+
instead of `FALSE` to indicate that all of the arguments of a Lua script must
|
460 |
+
be used to populate `ARGV[]`. This does not represent a breaking change.
|
461 |
+
|
462 |
+
- The `Predis\Helpers` class has been deprecated and it will be removed in
|
463 |
+
future releases.
|
464 |
+
|
465 |
+
|
466 |
+
v0.8.2 (2013-02-03)
|
467 |
+
================================================================================
|
468 |
+
|
469 |
+
- Added `Predis\Session\SessionHandler` to make it easy to store PHP sessions
|
470 |
+
on Redis using Predis. Please note that this class needs either PHP >= 5.4.0
|
471 |
+
or a polyfill for PHP's `SessionHandlerInterface`.
|
472 |
+
|
473 |
+
- Added the ability to get the default value of a client option directly from
|
474 |
+
`Predis\Option\ClientOption` using the `getDefault()` method by passing the
|
475 |
+
option name or its instance.
|
476 |
+
|
477 |
+
- __FIX__: the standard pipeline executor was not using the response parser
|
478 |
+
methods associated to commands to process raw responses (ISSUE #101).
|
479 |
+
|
480 |
+
|
481 |
+
v0.8.1 (2013-01-19)
|
482 |
+
================================================================================
|
483 |
+
|
484 |
+
- The `connections` client option can now accept a callable object returning
|
485 |
+
an instance of `Predis\Connection\ConnectionFactoryInterface`.
|
486 |
+
|
487 |
+
- Client options accepting callable objects as factories now pass their actual
|
488 |
+
instance to the callable as the second argument.
|
489 |
+
|
490 |
+
- `Predis\Command\Processor\KeyPrefixProcessor` can now be directly casted to
|
491 |
+
string to obtain the current prefix, useful with string interpolation.
|
492 |
+
|
493 |
+
- Added an optional callable argument to `Predis\Cluster\Distribution\HashRing`
|
494 |
+
and `Predis\Cluster\Distribution\KetamaPureRing` constructor that can be used
|
495 |
+
to customize how the distributor should extract the connection hash when
|
496 |
+
initializing the nodes distribution (ISSUE #36).
|
497 |
+
|
498 |
+
- Correctly handle `TTL` and `PTTL` returning -2 on non existing keys starting
|
499 |
+
with Redis 2.8.
|
500 |
+
|
501 |
+
- __FIX__: a missing use directive in `Predis\Transaction\MultiExecContext`
|
502 |
+
caused PHP errors when Redis did not return `+QUEUED` replies to commands
|
503 |
+
when inside a MULTI / EXEC context.
|
504 |
+
|
505 |
+
- __FIX__: the `parseResponse()` method implemented for a scripted command was
|
506 |
+
ignored when retrying to execute a Lua script by falling back to `EVAL` after
|
507 |
+
a `-NOSCRIPT` error (ISSUE #94).
|
508 |
+
|
509 |
+
- __FIX__: when subclassing `Predis\Client` the `getClientFor()` method returns
|
510 |
+
a new instance of the subclass instead of a new instance of `Predis\Client`.
|
511 |
+
|
512 |
+
|
513 |
+
v0.8.0 (2012-10-23)
|
514 |
+
================================================================================
|
515 |
+
|
516 |
+
- The default server profile for Redis is now `2.6`.
|
517 |
+
|
518 |
+
- Certain connection parameters have been renamed:
|
519 |
+
|
520 |
+
- `connection_async` is now `async_connect`
|
521 |
+
- `connection_timeout` is now `timeout`
|
522 |
+
- `connection_persistent` is now `persistent`
|
523 |
+
|
524 |
+
- The `throw_errors` connection parameter has been removed and replaced by the
|
525 |
+
new `exceptions` client option since exceptions on `-ERR` replies returned by
|
526 |
+
Redis are not generated by connection classes anymore but instead are thrown
|
527 |
+
by the client class and other abstractions such as pipeline contexts.
|
528 |
+
|
529 |
+
- Added smart support for redis-cluster (Redis v3.0) in addition to the usual
|
530 |
+
cluster implementation that uses client-side sharding.
|
531 |
+
|
532 |
+
- Various namespaces and classes have been renamed to follow rules inspired by
|
533 |
+
the Symfony2 naming conventions.
|
534 |
+
|
535 |
+
- The second argument of the constructor of `Predis\Client` does not accept
|
536 |
+
strings or instances of `Predis\Profile\ServerProfileInterface` anymore.
|
537 |
+
To specify a server profile you must explicitly set `profile` in the array
|
538 |
+
of client options.
|
539 |
+
|
540 |
+
- `Predis\Command\ScriptedCommand` internally relies on `EVALSHA` instead of
|
541 |
+
`EVAL` thus avoiding to send Lua scripts bodies on each request. The client
|
542 |
+
automatically resends the command falling back to `EVAL` when Redis returns a
|
543 |
+
`-NOSCRIPT` error. Automatic fallback to `EVAL` does not work with pipelines,
|
544 |
+
inside a `MULTI / EXEC` context or with plain `EVALSHA` commands.
|
545 |
+
|
546 |
+
- Complex responses are no more parsed by connection classes as they must be
|
547 |
+
processed by consumer classes using the handler associated to the issued
|
548 |
+
command. This means that executing commands directly on connections only
|
549 |
+
returns simple Redis types, but nothing changes when using `Predis\Client`
|
550 |
+
or the provided abstractions for pipelines and transactions.
|
551 |
+
|
552 |
+
- Iterators for multi-bulk replies now skip the response parsing method of the
|
553 |
+
command that generated the response and are passed directly to user code.
|
554 |
+
Pipeline and transaction objects still consume automatically iterators.
|
555 |
+
|
556 |
+
- Cluster and replication connections now extend a new common interface,
|
557 |
+
`Predis\Connection\AggregatedConnectionInterface`.
|
558 |
+
|
559 |
+
- `Predis\Connection\MasterSlaveReplication` now uses an external strategy
|
560 |
+
class to handle the logic for checking readable / writable commands and Lua
|
561 |
+
scripts.
|
562 |
+
|
563 |
+
- Command pipelines have been optimized for both speed and code cleanness, but
|
564 |
+
at the cost of bringing a breaking change in the signature of the interface
|
565 |
+
for pipeline executors.
|
566 |
+
|
567 |
+
- Added a new pipeline executor that sends commands wrapped in a MULTI / EXEC
|
568 |
+
context to make the execution atomic: if a pipeline fails at a certain point
|
569 |
+
then the whole pipeline is discarded.
|
570 |
+
|
571 |
+
- The key-hashing mechanism for commands is now handled externally and is no
|
572 |
+
more a competence of each command class. This change is neeeded to support
|
573 |
+
both client-side sharding and Redis cluster.
|
574 |
+
|
575 |
+
- `Predis\Options\Option` is now abstract, see `Predis\Option\AbstractOption`.
|
576 |
+
|
577 |
+
|
578 |
+
v0.7.3 (2012-06-01)
|
579 |
+
================================================================================
|
580 |
+
|
581 |
+
- New commands available in the Redis v2.6 profile (dev): `BITOP`, `BITCOUNT`.
|
582 |
+
|
583 |
+
- When the number of keys `Predis\Commands\ScriptedCommand` is negative, Predis
|
584 |
+
will count from the end of the arguments list to calculate the actual number
|
585 |
+
of keys that will be interpreted as elements for `KEYS` by the underlying
|
586 |
+
`EVAL` command.
|
587 |
+
|
588 |
+
- __FIX__: `examples\CustomDistributionStrategy.php` had a mistyped constructor
|
589 |
+
call and produced a bad distribution due to an error as pointed in ISSUE #63.
|
590 |
+
This bug is limited to the above mentioned example and does not affect the
|
591 |
+
classes implemented in the `Predis\Distribution` namespace.
|
592 |
+
|
593 |
+
- __FIX__: `Predis\Commands\ServerEvalSHA::getScriptHash()` was calculating the
|
594 |
+
hash while it just needs to return the first argument of the command.
|
595 |
+
|
596 |
+
- __FIX__: `Predis\Autoloader` has been modified to allow cascading autoloaders
|
597 |
+
for the `Predis` namespace.
|
598 |
+
|
599 |
+
|
600 |
+
v0.7.2 (2012-04-01)
|
601 |
+
================================================================================
|
602 |
+
|
603 |
+
- Added `2.6` in the server profiles aliases list for the upcoming Redis 2.6.
|
604 |
+
`2.4` is still the default server profile. `dev` now targets Redis 2.8.
|
605 |
+
|
606 |
+
- Connection instances can be serialized and unserialized using `serialize()`
|
607 |
+
and `unserialize()`. This is handy in certain scenarios such as client-side
|
608 |
+
clustering or replication to lower the overhead of initializing a connection
|
609 |
+
object with many sub-connections since unserializing them can be up to 5x
|
610 |
+
times faster.
|
611 |
+
|
612 |
+
- Reworked the default autoloader to make it faster. It is also possible to
|
613 |
+
prepend it in PHP's autoload stack.
|
614 |
+
|
615 |
+
- __FIX__: fixed parsing of the payload returned by `MONITOR` with Redis 2.6.
|
616 |
+
|
617 |
+
|
618 |
+
v0.7.1 (2011-12-27)
|
619 |
+
================================================================================
|
620 |
+
|
621 |
+
- The PEAR channel on PearHub has been deprecated in favour of `pear.nrk.io`.
|
622 |
+
|
623 |
+
- Miscellaneous minor fixes.
|
624 |
+
|
625 |
+
- Added transparent support for master / slave replication configurations where
|
626 |
+
write operations are performed on the master server and read operations are
|
627 |
+
routed to one of the slaves. Please refer to ISSUE #21 for a bit of history
|
628 |
+
and more details about replication support in Predis.
|
629 |
+
|
630 |
+
- The `profile` client option now accepts a callable object used to initialize
|
631 |
+
a new instance of `Predis\Profiles\IServerProfile`.
|
632 |
+
|
633 |
+
- Exposed a method for MULTI / EXEC contexts that adds the ability to execute
|
634 |
+
instances of Redis commands against transaction objects.
|
635 |
+
|
636 |
+
|
637 |
+
v0.7.0 (2011-12-11)
|
638 |
+
================================================================================
|
639 |
+
|
640 |
+
- Predis now adheres to the PSR-0 standard which means that there is no more a
|
641 |
+
single file holding all the classes of the library, but multiple files (one
|
642 |
+
for each class). You can use any PSR-0 compatible autoloader to load Predis
|
643 |
+
or just leverage the default one shipped with the library by requiring the
|
644 |
+
`Predis/Autoloader.php` and call `Predis\Autoloader::register()`.
|
645 |
+
|
646 |
+
- The default server profile for Redis is now 2.4. The `dev` profile supports
|
647 |
+
all the features of Redis 2.6 (currently unstable) such as Lua scripting.
|
648 |
+
|
649 |
+
- Support for long aliases (method names) for Redis commands has been dropped.
|
650 |
+
|
651 |
+
- Redis 1.0 is no more supported. From now on Predis will use only the unified
|
652 |
+
protocol to serialize commands.
|
653 |
+
|
654 |
+
- It is possible to prefix keys transparently on a client-level basis with the
|
655 |
+
new `prefix` client option.
|
656 |
+
|
657 |
+
- An external connection factory is used to initialize new connection instances
|
658 |
+
and developers can now register their own connection classes using the new
|
659 |
+
`connections` client option.
|
660 |
+
|
661 |
+
- It is possible to connect locally to Redis using UNIX domain sockets. Just
|
662 |
+
use `unix:///path/to/redis.sock` or a named array just like in the following
|
663 |
+
example: `array('scheme' => 'unix', 'path' => '/path/to/redis.sock');`.
|
664 |
+
|
665 |
+
- If the `phpiredis` extension is loaded by PHP, it is now possible to use an
|
666 |
+
alternative connection class that leverages it to make Predis faster on many
|
667 |
+
cases, especially when dealing with big multibulk replies, with the the only
|
668 |
+
downside that persistent connections are not supported. Please refer to the
|
669 |
+
documentation to see how to activate this class using the new `connections`
|
670 |
+
client option.
|
671 |
+
|
672 |
+
- Predis is capable to talk with Webdis, albeit with some limitations such as
|
673 |
+
the lack of pipelining and transactions, just by using the `http` scheme in
|
674 |
+
in the connection parameters. All is needed is PHP with the `curl` and the
|
675 |
+
`phpiredis` extensions loaded.
|
676 |
+
|
677 |
+
- Way too many changes in the public API to make a list here, we just tried to
|
678 |
+
make all the Redis commands compatible with previous releases of v0.6 so that
|
679 |
+
you do not have to worry if you are simply using Predis as a client. Probably
|
680 |
+
the only breaking changes that should be mentioned here are:
|
681 |
+
|
682 |
+
- `throw_on_error` has been renamed to `throw_errors` and it is a connection
|
683 |
+
parameter instead of a client option, along with `iterable_multibulk`.
|
684 |
+
|
685 |
+
- `key_distribution` has been removed from the client options. To customize
|
686 |
+
the distribution strategy you must provide a callable object to the new
|
687 |
+
`cluster` client option to configure and then return a new instance of
|
688 |
+
`Predis\Network\IConnectionCluster`.
|
689 |
+
|
690 |
+
- `Predis\Client::create()` has been removed. Just use the constructor to set
|
691 |
+
up a new instance of `Predis\Client`.
|
692 |
+
|
693 |
+
- `Predis\Client::pipelineSafe()` was deprecated in Predis v0.6.1 and now has
|
694 |
+
finally removed. Use `Predis\Client::pipeline(array('safe' => true))`.
|
695 |
+
|
696 |
+
- `Predis\Client::rawCommand()` has been removed due to inconsistencies with
|
697 |
+
the underlying connection abstractions. You can still get the raw resource
|
698 |
+
out of a connection with `Predis\Network\IConnectionSingle::getResource()`
|
699 |
+
so that you can talk directly with Redis.
|
700 |
+
|
701 |
+
- The `Predis\MultiBulkCommand` class has been merged into `Predis\Command` and
|
702 |
+
thus removed. Serialization of commands is now a competence of connections.
|
703 |
+
|
704 |
+
- The `Predis\IConnection` interface has been splitted into two new interfaces:
|
705 |
+
`Predis\Network\IConnectionSingle` and `Predis\Network\IConnectionCluster`.
|
706 |
+
|
707 |
+
- The constructor of `Predis\Client` now accepts more type of arguments such as
|
708 |
+
instances of `Predis\IConnectionParameters` and `Predis\Network\IConnection`.
|
709 |
+
|
710 |
+
|
711 |
+
v0.6.6 (2011-04-01)
|
712 |
+
================================================================================
|
713 |
+
|
714 |
+
- Switched to Redis 2.2 as the default server profile (there are no changes
|
715 |
+
that would break compatibility with previous releases). Long command names
|
716 |
+
are no more supported by default but if you need them you can still require
|
717 |
+
`Predis_Compatibility.php` to avoid breaking compatibility.
|
718 |
+
|
719 |
+
- Added a `VERSION` constant to `Predis\Client`.
|
720 |
+
|
721 |
+
- Some performance improvements for multibulk replies (parsing them is about
|
722 |
+
16% faster than the previous version). A few core classes have been heavily
|
723 |
+
optimized to reduce overhead when creating new instances.
|
724 |
+
|
725 |
+
- Predis now uses by default a new protocol reader, more lightweight and
|
726 |
+
faster than the default handler-based one. Users can revert to the old
|
727 |
+
protocol reader with the `reader` client option set to `composable`.
|
728 |
+
This client option can also accept custom reader classes implementing the
|
729 |
+
new `Predis\IResponseReader` interface.
|
730 |
+
|
731 |
+
- Added support for connecting to Redis using UNIX domain sockets (ISSUE #25).
|
732 |
+
|
733 |
+
- The `read_write_timeout` connection parameter can now be set to 0 or false
|
734 |
+
to disable read and write timeouts on connections. The old behaviour of -1
|
735 |
+
is still intact.
|
736 |
+
|
737 |
+
- `ZUNIONSTORE` and `ZINTERSTORE` can accept an array to specify a list of the
|
738 |
+
source keys to be used to populate the destination key.
|
739 |
+
|
740 |
+
- `MGET`, `SINTER`, `SUNION` and `SDIFF` can accept an array to specify a list
|
741 |
+
of keys. `SINTERSTORE`, `SUNIONSTORE` and `SDIFFSTORE` can also accept an
|
742 |
+
array to specify the list of source keys.
|
743 |
+
|
744 |
+
- `SUBSCRIBE` and `PSUBSCRIBE` can accept a list of channels for subscription.
|
745 |
+
|
746 |
+
- __FIX__: some client-side clean-ups for `MULTI/EXEC` were handled incorrectly
|
747 |
+
in a couple of corner cases (ISSUE #27).
|
748 |
+
|
749 |
+
|
750 |
+
v0.6.5 (2011-02-12)
|
751 |
+
================================================================================
|
752 |
+
|
753 |
+
- __FIX__: due to an untested internal change introduced in v0.6.4, a wrong
|
754 |
+
handling of bulk reads of zero-length values was producing protocol
|
755 |
+
desynchronization errors (ISSUE #20).
|
756 |
+
|
757 |
+
|
758 |
+
v0.6.4 (2011-02-12)
|
759 |
+
================================================================================
|
760 |
+
|
761 |
+
- Various performance improvements (15% ~ 25%) especially when dealing with
|
762 |
+
long multibulk replies or when using clustered connections.
|
763 |
+
|
764 |
+
- Added the `on_retry` option to `Predis\MultiExecBlock` that can be used to
|
765 |
+
specify an external callback (or any callable object) that gets invoked
|
766 |
+
whenever a transaction is aborted by the server.
|
767 |
+
|
768 |
+
- Added inline (p)subscribtion via options when initializing an instance of
|
769 |
+
`Predis\PubSubContext`.
|
770 |
+
|
771 |
+
|
772 |
+
v0.6.3 (2011-01-01)
|
773 |
+
================================================================================
|
774 |
+
|
775 |
+
- New commands available in the Redis v2.2 profile (dev):
|
776 |
+
- Strings: `SETRANGE`, `GETRANGE`, `SETBIT`, `GETBIT`
|
777 |
+
- Lists : `BRPOPLPUSH`
|
778 |
+
|
779 |
+
- The abstraction for `MULTI/EXEC` transactions has been dramatically improved
|
780 |
+
by providing support for check-and-set (CAS) operations when using Redis >=
|
781 |
+
2.2. Aborted transactions can also be optionally replayed in automatic up
|
782 |
+
to a user-defined number of times, after which a `Predis\AbortedMultiExec`
|
783 |
+
exception is thrown.
|
784 |
+
|
785 |
+
|
786 |
+
v0.6.2 (2010-11-28)
|
787 |
+
================================================================================
|
788 |
+
|
789 |
+
- Minor internal improvements and clean ups.
|
790 |
+
|
791 |
+
- New commands available in the Redis v2.2 profile (dev):
|
792 |
+
- Strings: `STRLEN`
|
793 |
+
- Lists : `LINSERT`, `RPUSHX`, `LPUSHX`
|
794 |
+
- ZSets : `ZREVRANGEBYSCORE`
|
795 |
+
- Misc. : `PERSIST`
|
796 |
+
|
797 |
+
- WATCH also accepts a single array parameter with the keys that should be
|
798 |
+
monitored during a transaction.
|
799 |
+
|
800 |
+
- Improved the behaviour of `Predis\MultiExecBlock` in certain corner cases.
|
801 |
+
|
802 |
+
- Improved parameters checking for the SORT command.
|
803 |
+
|
804 |
+
- __FIX__: the `STORE` parameter for the `SORT` command didn't work correctly
|
805 |
+
when using `0` as the target key (ISSUE #13).
|
806 |
+
|
807 |
+
- __FIX__: the methods for `UNWATCH` and `DISCARD` do not break anymore method
|
808 |
+
chaining with `Predis\MultiExecBlock`.
|
809 |
+
|
810 |
+
|
811 |
+
v0.6.1 (2010-07-11)
|
812 |
+
================================================================================
|
813 |
+
|
814 |
+
- Minor internal improvements and clean ups.
|
815 |
+
|
816 |
+
- New commands available in the Redis v2.2 profile (dev):
|
817 |
+
- Misc. : `WATCH`, `UNWATCH`
|
818 |
+
|
819 |
+
- Optional modifiers for `ZRANGE`, `ZREVRANGE` and `ZRANGEBYSCORE` queries are
|
820 |
+
supported using an associative array passed as the last argument of their
|
821 |
+
respective methods.
|
822 |
+
|
823 |
+
- The `LIMIT` modifier for `ZRANGEBYSCORE` can be specified using either:
|
824 |
+
- an indexed array: `array($offset, $count)`
|
825 |
+
- an associative array: `array('offset' => $offset, 'count' => $count)`
|
826 |
+
|
827 |
+
- The method `Predis\Client::__construct()` now accepts also instances of
|
828 |
+
`Predis\ConnectionParameters`.
|
829 |
+
|
830 |
+
- `Predis\MultiExecBlock` and `Predis\PubSubContext` now throw an exception
|
831 |
+
when trying to create their instances using a profile that does not
|
832 |
+
support the required Redis commands or when the client is connected to
|
833 |
+
a cluster of connections.
|
834 |
+
|
835 |
+
- Various improvements to `Predis\MultiExecBlock`:
|
836 |
+
- fixes and more consistent behaviour across various usage cases.
|
837 |
+
- support for `WATCH` and `UNWATCH` when using the current development
|
838 |
+
profile (Redis v2.2) and aborted transactions.
|
839 |
+
|
840 |
+
- New signature for `Predis\Client::multiExec()` which is now able to accept
|
841 |
+
an array of options for the underlying instance of `Predis\MultiExecBlock`.
|
842 |
+
Backwards compatibility with previous releases of Predis is ensured.
|
843 |
+
|
844 |
+
- New signature for `Predis\Client::pipeline()` which is now able to accept
|
845 |
+
an array of options for the underlying instance of Predis\CommandPipeline.
|
846 |
+
Backwards compatibility with previous releases of Predis is ensured.
|
847 |
+
The method `Predis\Client::pipelineSafe()` is to be considered deprecated.
|
848 |
+
|
849 |
+
- __FIX__: The `WEIGHT` modifier for `ZUNIONSTORE` and `ZINTERSTORE` was
|
850 |
+
handled incorrectly with more than two weights specified.
|
851 |
+
|
852 |
+
|
853 |
+
v0.6.0 (2010-05-24)
|
854 |
+
================================================================================
|
855 |
+
|
856 |
+
- Switched to the new multi-bulk request protocol for all of the commands
|
857 |
+
in the Redis 1.2 and Redis 2.0 profiles. Inline and bulk requests are now
|
858 |
+
deprecated as they will be removed in future releases of Redis.
|
859 |
+
|
860 |
+
- The default server profile is `2.0` (targeting Redis 2.0.x). If you are
|
861 |
+
using older versions of Redis, it is highly recommended that you specify
|
862 |
+
which server profile the client should use (e.g. `1.2` when connecting
|
863 |
+
to instances of Redis 1.2.x).
|
864 |
+
|
865 |
+
- Support for Redis 1.0 is now optional and it is provided by requiring
|
866 |
+
'Predis_Compatibility.php' before creating an instance of `Predis\Client`.
|
867 |
+
|
868 |
+
- New commands added to the Redis 2.0 profile since Predis 0.5.1:
|
869 |
+
- Strings: `SETEX`, `APPEND`, `SUBSTR`
|
870 |
+
- ZSets : `ZCOUNT`, `ZRANK`, `ZUNIONSTORE`, `ZINTERSTORE`, `ZREMBYRANK`,
|
871 |
+
`ZREVRANK`
|
872 |
+
- Hashes : `HSET`, `HSETNX`, `HMSET`, `HINCRBY`, `HGET`, `HMGET`, `HDEL`,
|
873 |
+
`HEXISTS`, `HLEN`, `HKEYS`, `HVALS`, `HGETALL`
|
874 |
+
- PubSub : `PUBLISH`, `SUBSCRIBE`, `UNSUBSCRIBE`
|
875 |
+
- Misc. : `DISCARD`, `CONFIG`
|
876 |
+
|
877 |
+
- Introduced client-level options with the new `Predis\ClientOptions` class.
|
878 |
+
Options can be passed to the constructor of `Predis\Client` in its second
|
879 |
+
argument as an array or an instance of `Predis\ClientOptions`. For brevity's
|
880 |
+
sake and compatibility with older versions, the constructor still accepts
|
881 |
+
an instance of `Predis\RedisServerProfile` in its second argument. The
|
882 |
+
currently supported client options are:
|
883 |
+
|
884 |
+
- `profile` [default: `2.0` as of Predis 0.6.0]: specifies which server
|
885 |
+
profile to use when connecting to Redis. This option accepts an instance
|
886 |
+
of `Predis\RedisServerProfile` or a string that indicates the version.
|
887 |
+
|
888 |
+
- `key_distribution` [default: `Predis\Distribution\HashRing`]: specifies
|
889 |
+
which key distribution strategy to use to distribute keys among the
|
890 |
+
servers that compose a cluster. This option accepts an instance of
|
891 |
+
`Predis\Distribution\IDistributionStrategy` so that users can implement
|
892 |
+
their own key distribution strategy. `Predis\Distribution\KetamaPureRing`
|
893 |
+
is an alternative distribution strategy providing a pure-PHP implementation
|
894 |
+
of the same algorithm used by libketama.
|
895 |
+
|
896 |
+
- `throw_on_error` [default: `TRUE`]: server errors can optionally be handled
|
897 |
+
"silently": instead of throwing an exception, the client returns an error
|
898 |
+
response type.
|
899 |
+
|
900 |
+
- `iterable_multibulk` [EXPERIMENTAL - default: `FALSE`]: in addition to the
|
901 |
+
classic way of fetching a whole multibulk reply into an array, the client
|
902 |
+
can now optionally stream a multibulk reply down to the user code by using
|
903 |
+
PHP iterators. It is just a little bit slower, but it can save a lot of
|
904 |
+
memory in certain scenarios.
|
905 |
+
|
906 |
+
- New parameters for connections:
|
907 |
+
|
908 |
+
- `alias` [default: not set]: every connection can now be identified by an
|
909 |
+
alias that is useful to get a specific connections when connected to a
|
910 |
+
cluster of Redis servers.
|
911 |
+
- `weight` [default: not set]: allows to balance keys asymmetrically across
|
912 |
+
multiple servers. This is useful when you have servers with different
|
913 |
+
amounts of memory to distribute the load of your keys accordingly.
|
914 |
+
- `connection_async` [default: `FALSE`]: estabilish connections to servers
|
915 |
+
in a non-blocking way, so that the client is not blocked while the socket
|
916 |
+
resource performs the actual connection.
|
917 |
+
- `connection_persistent` [default: `FALSE`]: the underlying socket resource
|
918 |
+
is left open when a script ends its lifecycle. Persistent connections can
|
919 |
+
lead to unpredictable or strange behaviours, so they should be used with
|
920 |
+
extreme care.
|
921 |
+
|
922 |
+
- Introduced the `Predis\Pipeline\IPipelineExecutor` interface. Classes that
|
923 |
+
implements this interface are used internally by the `Predis\CommandPipeline`
|
924 |
+
class to change the behaviour of the pipeline when writing/reading commands
|
925 |
+
from one or multiple servers. Here is the list of the default executors:
|
926 |
+
|
927 |
+
- `Predis\Pipeline\StandardExecutor`: exceptions generated by server errors
|
928 |
+
might be thrown depending on the options passed to the client (see the
|
929 |
+
`throw_on_error` client option). Instead, protocol or network errors always
|
930 |
+
throw exceptions. This is the default executor for single and clustered
|
931 |
+
connections and shares the same behaviour of Predis 0.5.x.
|
932 |
+
- `Predis\Pipeline\SafeExecutor`: exceptions generated by server, protocol
|
933 |
+
or network errors are not thrown but returned in the response array as
|
934 |
+
instances of `Predis\ResponseError` or `Predis\CommunicationException`.
|
935 |
+
- `Predis\Pipeline\SafeClusterExecutor`: this executor shares the same
|
936 |
+
behaviour of `Predis\Pipeline\SafeExecutor` but it is geared towards
|
937 |
+
clustered connections.
|
938 |
+
|
939 |
+
- Support for PUB/SUB is handled by the new `Predis\PubSubContext` class, which
|
940 |
+
could also be used to build a callback dispatcher for PUB/SUB scenarios.
|
941 |
+
|
942 |
+
- When connected to a cluster of connections, it is now possible to get a
|
943 |
+
new `Predis\Client` instance for a single connection of the cluster by
|
944 |
+
passing its alias/index to the new `Predis\Client::getClientFor()` method.
|
945 |
+
|
946 |
+
- `Predis\CommandPipeline` and `Predis\MultiExecBlock` return their instances
|
947 |
+
when invokink commands, thus allowing method chaining in pipelines and
|
948 |
+
multi-exec blocks.
|
949 |
+
|
950 |
+
- `Predis\MultiExecBlock` can handle the new `DISCARD` command.
|
951 |
+
|
952 |
+
- Connections now support float values for the `connection_timeout` parameter
|
953 |
+
to express timeouts with a microsecond resolution.
|
954 |
+
|
955 |
+
- __FIX__: TCP connections now respect the read/write timeout parameter when
|
956 |
+
reading the payload of server responses. Previously, `stream_get_contents()`
|
957 |
+
was being used internally to read data from a connection but it looks like
|
958 |
+
PHP does not honour the specified timeout for socket streams when inside
|
959 |
+
this function.
|
960 |
+
|
961 |
+
- __FIX__: The `GET` parameter for the `SORT` command now accepts also multiple
|
962 |
+
key patterns by passing an array of strings. (ISSUE #1).
|
963 |
+
|
964 |
+
* __FIX__: Replies to the `DEL` command return the number of elements deleted
|
965 |
+
by the server and not 0 or 1 interpreted as a boolean response. (ISSUE #4).
|
966 |
+
|
967 |
+
|
968 |
+
v0.5.1 (2010-01-23)
|
969 |
+
================================================================================
|
970 |
+
|
971 |
+
* `RPOPLPUSH` has been changed from bulk command to inline command in Redis
|
972 |
+
1.2.1, so `ListPopLastPushHead` now extends `InlineCommand`. The old behavior
|
973 |
+
is still available via the `ListPopLastPushHeadBulk` class so that you can
|
974 |
+
override the server profile if you need the old (and uncorrect) behaviour
|
975 |
+
when connecting to a Redis 1.2.0 instance.
|
976 |
+
|
977 |
+
* Added missing support for `BGREWRITEAOF` for Redis >= 1.2.0.
|
978 |
+
|
979 |
+
* Implemented a factory method for the `RedisServerProfile` class to ease the
|
980 |
+
creation of new server profile instances based on a version string.
|
981 |
+
|
982 |
+
|
983 |
+
v0.5.0 (2010-01-09)
|
984 |
+
================================================================================
|
985 |
+
* First versioned release of Predis
|
dependencies/vendor/predis/predis/CONTRIBUTING.md
ADDED
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
## Filing bug reports ##
|
2 |
+
|
3 |
+
Bugs or feature requests can be posted on the [GitHub issues](http://github.com/nrk/predis/issues)
|
4 |
+
section of the project.
|
5 |
+
|
6 |
+
When reporting bugs, in addition to the obvious description of your issue you __must__ always provide
|
7 |
+
some essential information about your environment such as:
|
8 |
+
|
9 |
+
1. version of Predis (check the `VERSION` file or the `Predis\Client::VERSION` constant).
|
10 |
+
2. version of Redis (check `redis_version` returned by [`INFO`](http://redis.io/commands/info)).
|
11 |
+
3. version of PHP.
|
12 |
+
4. name and version of the operating system.
|
13 |
+
5. when possible, a small snippet of code that reproduces the issue.
|
14 |
+
|
15 |
+
__Think about it__: we do not have a crystal ball and cannot predict things or peer into the unknown
|
16 |
+
so please provide as much details as possible to help us isolating issues and fix them.
|
17 |
+
|
18 |
+
__Never__ use GitHub issues to post generic questions about Predis! When you have questions about
|
19 |
+
how Predis works or how it can be used, please just hop me an email and I will get back to you as
|
20 |
+
soon as possible.
|
21 |
+
|
22 |
+
|
23 |
+
## Contributing code ##
|
24 |
+
|
25 |
+
If you want to work on Predis, it is highly recommended that you first run the test suite in order
|
26 |
+
to check that everything is OK and report strange behaviours or bugs. When modifying Predis please
|
27 |
+
make sure that no warnings or notices are emitted by PHP running the interpreter in your development
|
28 |
+
environment with the `error_reporting` variable set to `E_ALL | E_STRICT`.
|
29 |
+
|
30 |
+
The recommended way to contribute to Predis is to fork the project on GitHub, create topic branches
|
31 |
+
on your newly created repository to fix bugs or add new features (possibly with tests covering your
|
32 |
+
modifications) and then open a pull request with a description of the applied changes. Obviously you
|
33 |
+
can use any other Git hosting provider of your preference.
|
34 |
+
|
35 |
+
We always aim for consistency in our code base so you should follow basic coding rules as defined by
|
36 |
+
[PSR-1](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-1-basic-coding-standard.md)
|
37 |
+
and [PSR-2](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)
|
38 |
+
and stick with the conventions used in Predis to name classes and interfaces. Indentation should be
|
39 |
+
done with 4 spaces and code should be wrapped at 100 columns (please try to stay within this limit
|
40 |
+
even if the above mentioned official coding guidelines set the soft limit to 120 columns).
|
41 |
+
|
42 |
+
Please follow these [commit guidelines](http://git-scm.com/book/ch5-2.html#Commit-Guidelines) when
|
43 |
+
committing your code to Git and always write a meaningful (not necessarily extended) description of
|
44 |
+
your changes before opening pull requests.
|
dependencies/vendor/predis/predis/FAQ.md
ADDED
@@ -0,0 +1,177 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Some frequently asked questions about Predis #
|
2 |
+
________________________________________________
|
3 |
+
|
4 |
+
### What is the point of Predis? ###
|
5 |
+
|
6 |
+
The main point of Predis is about offering a highly customizable and extensible client for Redis,
|
7 |
+
that can be easily extended by developers while still being reasonabily fast. With Predis you can
|
8 |
+
swap almost any class with your own custom implementation: you can have custom connection classes,
|
9 |
+
new distribution strategies for client-side sharding, or handlers to replace or add Redis commands.
|
10 |
+
All of this can be achieved without messing with the source code of the library and directly in your
|
11 |
+
own application. Given the fast pace at which Redis is developed and adds new features, this can be
|
12 |
+
a great asset since it allows developers to add new and still missing features or commands or change
|
13 |
+
the standard behaviour of the library without the need to break dependencies in production code (at
|
14 |
+
least to some degree).
|
15 |
+
|
16 |
+
### Does Predis support UNIX domain sockets and persistent connections? ###
|
17 |
+
|
18 |
+
Yes. Obviously persistent connections actually work only when using PHP configured as a persistent
|
19 |
+
process reused by the web server (see [PHP-FPM](http://php-fpm.org)).
|
20 |
+
|
21 |
+
### Does Predis support SSL-encrypted connections? ###
|
22 |
+
|
23 |
+
Yes. Encrypted connections are mostly useful when connecting to Redis instances exposed by various
|
24 |
+
cloud hosting providers without the need to configure an SSL proxy, but you should also take into
|
25 |
+
account the general performances degradation especially during the connect() operation when the TLS
|
26 |
+
handshake must be performed to secure the connection. Persistent SSL-encrypted connections may help
|
27 |
+
in that respect, but they are supported only when running on PHP >= 7.0.0.
|
28 |
+
|
29 |
+
### Does Predis support transparent (de)serialization of values? ###
|
30 |
+
|
31 |
+
No and it will not ever do that by default. The reason behind this decision is that serialization is
|
32 |
+
usually something that developers prefer to customize depending on their needs and can not be easily
|
33 |
+
generalized when using Redis because of the many possible access patterns for your data. This does
|
34 |
+
not mean that it is impossible to have such a feature since you can leverage the extensibility of
|
35 |
+
this library to define your own serialization-aware commands. You can find more details about how to
|
36 |
+
do that [on this issue](http://github.com/nrk/predis/issues/29#issuecomment-1202624).
|
37 |
+
|
38 |
+
### How can I force Predis to connect to Redis before sending any command? ###
|
39 |
+
|
40 |
+
Explicitly connecting to Redis is usually not needed since the client initializes connections lazily
|
41 |
+
only when they are needed. Admittedly, this behavior can be inconvenient in certain scenarios when
|
42 |
+
you absolutely need to perform an upfront check to determine if the server is up and running and
|
43 |
+
eventually catch exceptions on failures. Forcing the client to open the underlying connection can be
|
44 |
+
done by invoking `Predis\Client::connect()`:
|
45 |
+
|
46 |
+
```php
|
47 |
+
$client = new Predis\Client();
|
48 |
+
|
49 |
+
try {
|
50 |
+
$client->connect();
|
51 |
+
} catch (Predis\Connection\ConnectionException $exception) {
|
52 |
+
// We could not connect to Redis! Your handling code goes here.
|
53 |
+
}
|
54 |
+
|
55 |
+
$client->info();
|
56 |
+
```
|
57 |
+
|
58 |
+
### How Predis abstracts Redis commands? ###
|
59 |
+
|
60 |
+
The approach used to implement Redis commands is quite simple: by default each command follows the
|
61 |
+
same signature as defined on the [Redis documentation](http://redis.io/commands) which makes things
|
62 |
+
pretty easy if you already know how Redis works or you need to look up how to use certain commands.
|
63 |
+
Alternatively, variadic commands can accept an array for keys or values (depending on the command)
|
64 |
+
instead of a list of arguments. Commands such as [`RPUSH`](http://redis.io/commands/rpush) and
|
65 |
+
[`HMSET`](http://redis.io/commands/hmset) are great examples:
|
66 |
+
|
67 |
+
```php
|
68 |
+
$client->rpush('my:list', 'value1', 'value2', 'value3'); // plain method arguments
|
69 |
+
$client->rpush('my:list', ['value1', 'value2', 'value3']); // single argument array
|
70 |
+
|
71 |
+
$client->hmset('my:hash', 'field1', 'value1', 'field2', 'value2'); // plain method arguments
|
72 |
+
$client->hmset('my:hash', ['field1'=>'value1', 'field2'=>'value2']); // single named array
|
73 |
+
```
|
74 |
+
|
75 |
+
An exception to this rule is [`SORT`](http://redis.io/commands/sort) for which modifiers are passed
|
76 |
+
[using a named array](tests/Predis/Command/KeySortTest.php#L54-L75).
|
77 |
+
|
78 |
+
|
79 |
+
# Speaking about performances... #
|
80 |
+
_________________________________________________
|
81 |
+
|
82 |
+
|
83 |
+
### Predis is a pure-PHP implementation: it can not be fast enough! ###
|
84 |
+
|
85 |
+
It really depends, but most of the times the answer is: _yes, it is fast enough_. I will give you a
|
86 |
+
couple of easy numbers with a simple test that uses a single client and is executed by PHP 5.5.6
|
87 |
+
against a local instance of Redis 2.8 that runs under Ubuntu 13.10 on a Intel Q6600:
|
88 |
+
|
89 |
+
```
|
90 |
+
21000 SET/sec using 12 bytes for both key and value.
|
91 |
+
21000 GET/sec while retrieving the very same values.
|
92 |
+
0.130 seconds to fetch 30000 keys using _KEYS *_.
|
93 |
+
```
|
94 |
+
|
95 |
+
How does it compare with [__phpredis__](http://github.com/nicolasff/phpredis), a nice C extension
|
96 |
+
providing an efficient client for Redis?
|
97 |
+
|
98 |
+
```
|
99 |
+
30100 SET/sec using 12 bytes for both key and value
|
100 |
+
29400 GET/sec while retrieving the very same values
|
101 |
+
0.035 seconds to fetch 30000 keys using "KEYS *"".
|
102 |
+
```
|
103 |
+
|
104 |
+
Wow __phpredis__ seems much faster! Well, we are comparing a C extension with a pure-PHP library so
|
105 |
+
lower numbers are quite expected but there is a fundamental flaw in them: is this really how you are
|
106 |
+
going to use Redis in your application? Are you really going to send thousands of commands using a
|
107 |
+
for-loop on each page request using a single client instance? If so... well I guess you are probably
|
108 |
+
doing something wrong. Also, if you need to `SET` or `GET` multiple keys you should definitely use
|
109 |
+
commands such as `MSET` and `MGET`. You can also use pipelining to get more performances when this
|
110 |
+
technique can be used.
|
111 |
+
|
112 |
+
There is one more thing: we have tested the overhead of Predis by connecting on a localhost instance
|
113 |
+
of Redis but how these numbers change when we hit the physical network by connecting to remote Redis
|
114 |
+
instances?
|
115 |
+
|
116 |
+
```
|
117 |
+
Using Predis:
|
118 |
+
3200 SET/sec using 12 bytes for both key and value
|
119 |
+
3200 GET/sec while retrieving the very same values
|
120 |
+
0.132 seconds to fetch 30000 keys using "KEYS *".
|
121 |
+
|
122 |
+
Using phpredis:
|
123 |
+
3500 SET/sec using 12 bytes for both key and value
|
124 |
+
3500 GET/sec while retrieving the very same values
|
125 |
+
0.045 seconds to fetch 30000 keys using "KEYS *".
|
126 |
+
```
|
127 |
+
|
128 |
+
There you go, you get almost the same average numbers and the reason is simple: network latency is a
|
129 |
+
real performance killer and you cannot do (almost) anything about that. As a disclaimer, remember
|
130 |
+
that we are measuring the overhead of client libraries implementations and the effects of network
|
131 |
+
round-trip times, so we are not really measuring how fast Redis is. Redis shines best with thousands
|
132 |
+
of concurrent clients doing requests! Also, actual performances should be measured according to how
|
133 |
+
your application will use Redis.
|
134 |
+
|
135 |
+
### I am convinced, but performances for multi-bulk responses are still worse ###
|
136 |
+
|
137 |
+
Fair enough, but there is an option available if you need even more speed and consists on installing
|
138 |
+
__[phpiredis](http://github.com/nrk/phpiredis)__ (note the additional _i_ in the name) and let the
|
139 |
+
client use it. __phpiredis__ is another C extension that wraps __hiredis__ (the official C client
|
140 |
+
library for Redis) with a thin layer exposing its features to PHP. You can then choose between two
|
141 |
+
different connection classes:
|
142 |
+
|
143 |
+
- `Predis\Connection\PhpiredisStreamConnection` (using native PHP streams).
|
144 |
+
- `Predis\Connection\PhpiredisSocketConnection` (requires `ext-socket`).
|
145 |
+
|
146 |
+
You will now get the benefits of a faster protocol serializer and parser just by adding a couple of
|
147 |
+
lines of code:
|
148 |
+
|
149 |
+
```php
|
150 |
+
$client = new Predis\Client('tcp://127.0.0.1', array(
|
151 |
+
'connections' => array(
|
152 |
+
'tcp' => 'Predis\Connection\PhpiredisStreamConnection',
|
153 |
+
'unix' => 'Predis\Connection\PhpiredisSocketConnection',
|
154 |
+
),
|
155 |
+
));
|
156 |
+
```
|
157 |
+
|
158 |
+
Dead simple. Nothing changes in the way you use the library in your application. So how fast is it
|
159 |
+
our basic benchmark script now? There are not much improvements for inline or short bulk responses
|
160 |
+
like the ones returned by `SET` and `GET`, but the speed for parsing multi-bulk responses is now on
|
161 |
+
par with phpredis:
|
162 |
+
|
163 |
+
```
|
164 |
+
Fatching 30000 keys with _KEYS *_ using Predis paired with phpiredis::
|
165 |
+
|
166 |
+
0.035 seconds from a local Redis instance
|
167 |
+
0.047 seconds from a remote Redis instance
|
168 |
+
```
|
169 |
+
|
170 |
+
### If I need an extension to get better performances, why not using phpredis? ###
|
171 |
+
|
172 |
+
Good question. Generically speaking if you need absolute uber-speed using Redis on the localhost and
|
173 |
+
you do not care about abstractions built around some Redis features such as MULTI / EXEC, or if you
|
174 |
+
do not need any kind of extensibility or guaranteed backwards compatibility with different versions
|
175 |
+
of Redis (Predis currently supports from 1.2 up to 2.8 and the current development version), then
|
176 |
+
using __phpredis__ makes absolutely sense. Otherwise, Predis is perfect for the job and by adding
|
177 |
+
__phpiredis__ you can get a nice speed bump almost for free.
|
{includes → dependencies/vendor/predis}/predis/LICENSE
RENAMED
File without changes
|
dependencies/vendor/predis/predis/README.md
ADDED
@@ -0,0 +1,492 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Predis #
|
2 |
+
|
3 |
+
[![Software license][ico-license]](LICENSE)
|
4 |
+
[![Latest stable][ico-version-stable]][link-packagist]
|
5 |
+
[![Latest development][ico-version-dev]][link-packagist]
|
6 |
+
[![Monthly installs][ico-downloads-monthly]][link-downloads]
|
7 |
+
[![Build status][ico-travis]][link-travis]
|
8 |
+
[![HHVM support][ico-hhvm]][link-hhvm]
|
9 |
+
[![Gitter room][ico-gitter]][link-gitter]
|
10 |
+
|
11 |
+
Flexible and feature-complete [Redis](http://redis.io) client for PHP >= 5.3 and HHVM >= 2.3.0.
|
12 |
+
|
13 |
+
Predis does not require any additional C extension by default, but it can be optionally paired with
|
14 |
+
[phpiredis](https://github.com/nrk/phpiredis) to lower the overhead of the serialization and parsing
|
15 |
+
of the [Redis RESP Protocol](http://redis.io/topics/protocol). For an __experimental__ asynchronous
|
16 |
+
implementation of the client you can refer to [Predis\Async](https://github.com/nrk/predis-async).
|
17 |
+
|
18 |
+
More details about this project can be found on the [frequently asked questions](FAQ.md).
|
19 |
+
|
20 |
+
|
21 |
+
## Main features ##
|
22 |
+
|
23 |
+
- Support for different versions of Redis (from __2.0__ to __3.2__) using profiles.
|
24 |
+
- Support for clustering using client-side sharding and pluggable keyspace distributors.
|
25 |
+
- Support for [redis-cluster](http://redis.io/topics/cluster-tutorial) (Redis >= 3.0).
|
26 |
+
- Support for master-slave replication setups and [redis-sentinel](http://redis.io/topics/sentinel).
|
27 |
+
- Transparent key prefixing of keys using a customizable prefix strategy.
|
28 |
+
- Command pipelining on both single nodes and clusters (client-side sharding only).
|
29 |
+
- Abstraction for Redis transactions (Redis >= 2.0) and CAS operations (Redis >= 2.2).
|
30 |
+
- Abstraction for Lua scripting (Redis >= 2.6) and automatic switching between `EVALSHA` or `EVAL`.
|
31 |
+
- Abstraction for `SCAN`, `SSCAN`, `ZSCAN` and `HSCAN` (Redis >= 2.8) based on PHP iterators.
|
32 |
+
- Connections are established lazily by the client upon the first command and can be persisted.
|
33 |
+
- Connections can be established via TCP/IP (also TLS/SSL-encrypted) or UNIX domain sockets.
|
34 |
+
- Support for [Webdis](http://webd.is) (requires both `ext-curl` and `ext-phpiredis`).
|
35 |
+
- Support for custom connection classes for providing different network or protocol backends.
|
36 |
+
- Flexible system for defining custom commands and profiles and override the default ones.
|
37 |
+
|
38 |
+
|
39 |
+
## How to _install_ and use Predis ##
|
40 |
+
|
41 |
+
This library can be found on [Packagist](http://packagist.org/packages/predis/predis) for an easier
|
42 |
+
management of projects dependencies using [Composer](http://packagist.org/about-composer) or on our
|
43 |
+
[own PEAR channel](http://pear.nrk.io) for a more traditional installation using PEAR. Ultimately,
|
44 |
+
compressed archives of each release are [available on GitHub](https://github.com/nrk/predis/tags).
|
45 |
+
|
46 |
+
|
47 |
+
### Loading the library ###
|
48 |
+
|
49 |
+
Predis relies on the autoloading features of PHP to load its files when needed and complies with the
|
50 |
+
[PSR-4 standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader.md).
|
51 |
+
Autoloading is handled automatically when dependencies are managed through Composer, but it is also
|
52 |
+
possible to leverage its own autoloader in projects or scripts lacking any autoload facility:
|
53 |
+
|
54 |
+
```php
|
55 |
+
// Prepend a base path if Predis is not available in your "include_path".
|
56 |
+
require 'Predis/Autoloader.php';
|
57 |
+
|
58 |
+
Predis\Autoloader::register();
|
59 |
+
```
|
60 |
+
|
61 |
+
It is also possible to create a [phar](http://www.php.net/manual/en/intro.phar.php) archive directly
|
62 |
+
from the repository by launching the `bin/create-phar` script. The generated phar already contains a
|
63 |
+
stub defining its own autoloader, so you just need to `require()` it to start using the library.
|
64 |
+
|
65 |
+
|
66 |
+
### Connecting to Redis ###
|
67 |
+
|
68 |
+
When creating a client instance without passing any connection parameter, Predis assumes `127.0.0.1`
|
69 |
+
and `6379` as default host and port. The default timeout for the `connect()` operation is 5 seconds:
|
70 |
+
|
71 |
+
```php
|
72 |
+
$client = new Predis\Client();
|
73 |
+
$client->set('foo', 'bar');
|
74 |
+
$value = $client->get('foo');
|
75 |
+
```
|
76 |
+
|
77 |
+
Connection parameters can be supplied either in the form of URI strings or named arrays. The latter
|
78 |
+
is the preferred way to supply parameters, but URI strings can be useful when parameters are read
|
79 |
+
from non-structured or partially-structured sources:
|
80 |
+
|
81 |
+
```php
|
82 |
+
// Parameters passed using a named array:
|
83 |
+
$client = new Predis\Client([
|
84 |
+
'scheme' => 'tcp',
|
85 |
+
'host' => '10.0.0.1',
|
86 |
+
'port' => 6379,
|
87 |
+
]);
|
88 |
+
|
89 |
+
// Same set of parameters, passed using an URI string:
|
90 |
+
$client = new Predis\Client('tcp://10.0.0.1:6379');
|
91 |
+
```
|
92 |
+
|
93 |
+
It is also possible to connect to local instances of Redis using UNIX domain sockets, in this case
|
94 |
+
the parameters must use the `unix` scheme and specify a path for the socket file:
|
95 |
+
|
96 |
+
```php
|
97 |
+
$client = new Predis\Client(['scheme' => 'unix', 'path' => '/path/to/redis.sock']);
|
98 |
+
$client = new Predis\Client('unix:/path/to/redis.sock');
|
99 |
+
```
|
100 |
+
|
101 |
+
The client can leverage TLS/SSL encryption to connect to secured remote Redis instances without the
|
102 |
+
need to configure an SSL proxy like stunnel. This can be useful when connecting to nodes running on
|
103 |
+
various cloud hosting providers. Encryption can be enabled with using the `tls` scheme and an array
|
104 |
+
of suitable [options](http://php.net/manual/context.ssl.php) passed via the `ssl` parameter:
|
105 |
+
|
106 |
+
```php
|
107 |
+
// Named array of connection parameters:
|
108 |
+
$client = new Predis\Client([
|
109 |
+
'scheme' => 'tls',
|
110 |
+
'ssl' => ['cafile' => 'private.pem', 'verify_peer' => true],
|
111 |
+
]
|
112 |
+
|
113 |
+
// Same set of parameters, but using an URI string:
|
114 |
+
$client = new Predis\Client('tls://127.0.0.1?ssl[cafile]=private.pem&ssl[verify_peer]=1');
|
115 |
+
```
|
116 |
+
|
117 |
+
The connection schemes [`redis`](http://www.iana.org/assignments/uri-schemes/prov/redis) (alias of
|
118 |
+
`tcp`) and [`rediss`](http://www.iana.org/assignments/uri-schemes/prov/rediss) (alias of `tls`) are
|
119 |
+
also supported, with the difference that URI strings containing these schemes are parsed following
|
120 |
+
the rules described on their respective IANA provisional registration documents.
|
121 |
+
|
122 |
+
The actual list of supported connection parameters can vary depending on each connection backend so
|
123 |
+
it is recommended to refer to their specific documentation or implementation for details.
|
124 |
+
|
125 |
+
When an array of connection parameters is provided, Predis automatically works in cluster mode using
|
126 |
+
client-side sharding. Both named arrays and URI strings can be mixed when providing configurations
|
127 |
+
for each node:
|
128 |
+
|
129 |
+
```php
|
130 |
+
$client = new Predis\Client([
|
131 |
+
'tcp://10.0.0.1?alias=first-node',
|
132 |
+
['host' => '10.0.0.2', 'alias' => 'second-node'],
|
133 |
+
]);
|
134 |
+
```
|
135 |
+
|
136 |
+
See the [aggregate connections](#aggregate-connections) section of this document for more details.
|
137 |
+
|
138 |
+
Connections to Redis are lazy meaning that the client connects to a server only if and when needed.
|
139 |
+
While it is recommended to let the client do its own stuff under the hood, there may be times when
|
140 |
+
it is still desired to have control of when the connection is opened or closed: this can easily be
|
141 |
+
achieved by invoking `$client->connect()` and `$client->disconnect()`. Please note that the effect
|
142 |
+
of these methods on aggregate connections may differ depending on each specific implementation.
|
143 |
+
|
144 |
+
|
145 |
+
### Client configuration ###
|
146 |
+
|
147 |
+
Many aspects and behaviors of the client can be configured by passing specific client options to the
|
148 |
+
second argument of `Predis\Client::__construct()`:
|
149 |
+
|
150 |
+
```php
|
151 |
+
$client = new Predis\Client($parameters, ['profile' => '2.8', 'prefix' => 'sample:']);
|
152 |
+
```
|
153 |
+
|
154 |
+
Options are managed using a mini DI-alike container and their values can be lazily initialized only
|
155 |
+
when needed. The client options supported by default in Predis are:
|
156 |
+
|
157 |
+
- `profile`: specifies the profile to use to match a specific version of Redis.
|
158 |
+
- `prefix`: prefix string automatically applied to keys found in commands.
|
159 |
+
- `exceptions`: whether the client should throw or return responses upon Redis errors.
|
160 |
+
- `connections`: list of connection backends or a connection factory instance.
|
161 |
+
- `cluster`: specifies a cluster backend (`predis`, `redis` or callable object).
|
162 |
+
- `replication`: specifies a replication backend (`TRUE`, `sentinel` or callable object).
|
163 |
+
- `aggregate`: overrides `cluster` and `replication` to provide a custom connections aggregator.
|
164 |
+
- `parameters`: list of default connection parameters for aggregate connections.
|
165 |
+
|
166 |
+
Users can also provide custom options with values or callable objects (for lazy initialization) that
|
167 |
+
are stored in the options container for later use through the library.
|
168 |
+
|
169 |
+
|
170 |
+
### Aggregate connections ###
|
171 |
+
|
172 |
+
Aggregate connections are the foundation upon which Predis implements clustering and replication and
|
173 |
+
they are used to group multiple connections to single Redis nodes and hide the specific logic needed
|
174 |
+
to handle them properly depending on the context. Aggregate connections usually require an array of
|
175 |
+
connection parameters when creating a new client instance.
|
176 |
+
|
177 |
+
#### Cluster ####
|
178 |
+
|
179 |
+
By default, when no specific client options are set and an array of connection parameters is passed
|
180 |
+
to the client's constructor, Predis configures itself to work in clustering mode using a traditional
|
181 |
+
client-side sharding approach to create a cluster of independent nodes and distribute the keyspace
|
182 |
+
among them. This approach needs some form of external health monitoring of nodes and requires manual
|
183 |
+
operations to rebalance the keyspace when changing its configuration by adding or removing nodes:
|
184 |
+
|
185 |
+
```php
|
186 |
+
$parameters = ['tcp://10.0.0.1', 'tcp://10.0.0.2', 'tcp://10.0.0.3'];
|
187 |
+
|
188 |
+
$client = new Predis\Client($parameters);
|
189 |
+
```
|
190 |
+
|
191 |
+
Along with Redis 3.0, a new supervised and coordinated type of clustering was introduced in the form
|
192 |
+
of [redis-cluster](http://redis.io/topics/cluster-tutorial). This kind of approach uses a different
|
193 |
+
algorithm to distribute the keyspaces, with Redis nodes coordinating themselves by communicating via
|
194 |
+
a gossip protocol to handle health status, rebalancing, nodes discovery and request redirection. In
|
195 |
+
order to connect to a cluster managed by redis-cluster, the client requires a list of its nodes (not
|
196 |
+
necessarily complete since it will automatically discover new nodes if necessary) and the `cluster`
|
197 |
+
client options set to `redis`:
|
198 |
+
|
199 |
+
```php
|
200 |
+
$parameters = ['tcp://10.0.0.1', 'tcp://10.0.0.2', 'tcp://10.0.0.3'];
|
201 |
+
$options = ['cluster' => 'redis'];
|
202 |
+
|
203 |
+
$client = new Predis\Client($parameters, $options);
|
204 |
+
```
|
205 |
+
|
206 |
+
#### Replication ####
|
207 |
+
|
208 |
+
The client can be configured to operate in a single master / multiple slaves setup to provide better
|
209 |
+
service availability. When using replication, Predis recognizes read-only commands and sends them to
|
210 |
+
a random slave in order to provide some sort of load-balancing and switches to the master as soon as
|
211 |
+
it detects a command that performs any kind of operation that would end up modifying the keyspace or
|
212 |
+
the value of a key. Instead of raising a connection error when a slave fails, the client attempts to
|
213 |
+
fall back to a different slave among the ones provided in the configuration.
|
214 |
+
|
215 |
+
The basic configuration needed to use the client in replication mode requires one Redis server to be
|
216 |
+
identified as the master (this can be done via connection parameters using the `alias` parameter set
|
217 |
+
to `master`) and one or more servers acting as slaves:
|
218 |
+
|
219 |
+
```php
|
220 |
+
$parameters = ['tcp://10.0.0.1?alias=master', 'tcp://10.0.0.2', 'tcp://10.0.0.3'];
|
221 |
+
$options = ['replication' => true];
|
222 |
+
|
223 |
+
$client = new Predis\Client($parameters, $options);
|
224 |
+
```
|
225 |
+
|
226 |
+
The above configuration has a static list of servers and relies entirely on the client's logic, but
|
227 |
+
it is possible to rely on [`redis-sentinel`](http://redis.io/topics/sentinel) for a more robust HA
|
228 |
+
environment with sentinel servers acting as a source of authority for clients for service discovery.
|
229 |
+
The minimum configuration required by the client to work with redis-sentinel is a list of connection
|
230 |
+
parameters pointing to a bunch of sentinel instances, the `replication` option set to `sentinel` and
|
231 |
+
the `service` option set to the name of the service:
|
232 |
+
|
233 |
+
```php
|
234 |
+
$sentinels = ['tcp://10.0.0.1', 'tcp://10.0.0.2', 'tcp://10.0.0.3'];
|
235 |
+
$options = ['replication' => 'sentinel', 'service' => 'mymaster'];
|
236 |
+
|
237 |
+
$client = new Predis\Client($sentinels, $options);
|
238 |
+
```
|
239 |
+
|
240 |
+
If the master and slave nodes are configured to require an authentication from clients, a password
|
241 |
+
must be provided via the global `parameters` client option. This option can also be used to specify
|
242 |
+
a different database index. The client options array would then look like this:
|
243 |
+
|
244 |
+
```php
|
245 |
+
$options = [
|
246 |
+
'replication' => 'sentinel',
|
247 |
+
'service' => 'mymaster',
|
248 |
+
'parameters' => [
|
249 |
+
'password' => $secretpassword,
|
250 |
+
'database' => 10,
|
251 |
+
],
|
252 |
+
];
|
253 |
+
```
|
254 |
+
|
255 |
+
While Predis is able to distinguish commands performing write and read-only operations, `EVAL` and
|
256 |
+
`EVALSHA` represent a corner case in which the client switches to the master node because it cannot
|
257 |
+
tell when a Lua script is safe to be executed on slaves. While this is indeed the default behavior,
|
258 |
+
when certain Lua scripts do not perform write operations it is possible to provide an hint to tell
|
259 |
+
the client to stick with slaves for their execution:
|
260 |
+
|
261 |
+
```php
|
262 |
+
$parameters = ['tcp://10.0.0.1?alias=master', 'tcp://10.0.0.2', 'tcp://10.0.0.3'];
|
263 |
+
$options = ['replication' => function () {
|
264 |
+
// Set scripts that won't trigger a switch from a slave to the master node.
|
265 |
+
$strategy = new Predis\Replication\ReplicationStrategy();
|
266 |
+
$strategy->setScriptReadOnly($LUA_SCRIPT);
|
267 |
+
|
268 |
+
return new Predis\Connection\Aggregate\MasterSlaveReplication($strategy);
|
269 |
+
}];
|
270 |
+
|
271 |
+
$client = new Predis\Client($parameters, $options);
|
272 |
+
$client->eval($LUA_SCRIPT, 0); // Sticks to slave using `eval`...
|
273 |
+
$client->evalsha(sha1($LUA_SCRIPT), 0); // ... and `evalsha`, too.
|
274 |
+
```
|
275 |
+
|
276 |
+
The [`examples`](examples/) directory contains a few scripts that demonstrate how the client can be
|
277 |
+
configured and used to leverage replication in both basic and complex scenarios.
|
278 |
+
|
279 |
+
|
280 |
+
### Command pipelines ###
|
281 |
+
|
282 |
+
Pipelining can help with performances when many commands need to be sent to a server by reducing the
|
283 |
+
latency introduced by network round-trip timings. Pipelining also works with aggregate connections.
|
284 |
+
The client can execute the pipeline inside a callable block or return a pipeline instance with the
|
285 |
+
ability to chain commands thanks to its fluent interface:
|
286 |
+
|
287 |
+
```php
|
288 |
+
// Executes a pipeline inside the given callable block:
|
289 |
+
$responses = $client->pipeline(function ($pipe) {
|
290 |
+
for ($i = 0; $i < 1000; $i++) {
|
291 |
+
$pipe->set("key:$i", str_pad($i, 4, '0', 0));
|
292 |
+
$pipe->get("key:$i");
|
293 |
+
}
|
294 |
+
});
|
295 |
+
|
296 |
+
// Returns a pipeline that can be chained thanks to its fluent interface:
|
297 |
+
$responses = $client->pipeline()->set('foo', 'bar')->get('foo')->execute();
|
298 |
+
```
|
299 |
+
|
300 |
+
|
301 |
+
### Transactions ###
|
302 |
+
|
303 |
+
The client provides an abstraction for Redis transactions based on `MULTI` and `EXEC` with a similar
|
304 |
+
interface to command pipelines:
|
305 |
+
|
306 |
+
```php
|
307 |
+
// Executes a transaction inside the given callable block:
|
308 |
+
$responses = $client->transaction(function ($tx) {
|
309 |
+
$tx->set('foo', 'bar');
|
310 |
+
$tx->get('foo');
|
311 |
+
});
|
312 |
+
|
313 |
+
// Returns a transaction that can be chained thanks to its fluent interface:
|
314 |
+
$responses = $client->transaction()->set('foo', 'bar')->get('foo')->execute();
|
315 |
+
```
|
316 |
+
|
317 |
+
This abstraction can perform check-and-set operations thanks to `WATCH` and `UNWATCH` and provides
|
318 |
+
automatic retries of transactions aborted by Redis when `WATCH`ed keys are touched. For an example
|
319 |
+
of a transaction using CAS you can see [the following example](examples/transaction_using_cas.php).
|
320 |
+
|
321 |
+
|
322 |
+
### Adding new commands ###
|
323 |
+
|
324 |
+
While we try to update Predis to stay up to date with all the commands available in Redis, you might
|
325 |
+
prefer to stick with an old version of the library or provide a different way to filter arguments or
|
326 |
+
parse responses for specific commands. To achieve that, Predis provides the ability to implement new
|
327 |
+
command classes to define or override commands in the default server profiles used by the client:
|
328 |
+
|
329 |
+
```php
|
330 |
+
// Define a new command by extending Predis\Command\Command:
|
331 |
+
class BrandNewRedisCommand extends Predis\Command\Command
|
332 |
+
{
|
333 |
+
public function getId()
|
334 |
+
{
|
335 |
+
return 'NEWCMD';
|
336 |
+
}
|
337 |
+
}
|
338 |
+
|
339 |
+
// Inject your command in the current profile:
|
340 |
+
$client = new Predis\Client();
|
341 |
+
$client->getProfile()->defineCommand('newcmd', 'BrandNewRedisCommand');
|
342 |
+
|
343 |
+
$response = $client->newcmd();
|
344 |
+
```
|
345 |
+
|
346 |
+
There is also a method to send raw commands without filtering their arguments or parsing responses.
|
347 |
+
Users must provide the list of arguments for the command as an array, following the signatures as
|
348 |
+
defined by the [Redis documentation for commands](http://redis.io/commands):
|
349 |
+
|
350 |
+
```php
|
351 |
+
$response = $client->executeRaw(['SET', 'foo', 'bar']);
|
352 |
+
```
|
353 |
+
|
354 |
+
|
355 |
+
### Script commands ###
|
356 |
+
|
357 |
+
While it is possible to leverage [Lua scripting](http://redis.io/commands/eval) on Redis 2.6+ using
|
358 |
+
directly [`EVAL`](http://redis.io/commands/eval) and [`EVALSHA`](http://redis.io/commands/evalsha),
|
359 |
+
Predis offers script commands as an higher level abstraction built upon them to make things simple.
|
360 |
+
Script commands can be registered in the server profile used by the client and are accessible as if
|
361 |
+
they were plain Redis commands, but they define Lua scripts that get transmitted to the server for
|
362 |
+
remote execution. Internally they use [`EVALSHA`](http://redis.io/commands/evalsha) by default and
|
363 |
+
identify a script by its SHA1 hash to save bandwidth, but [`EVAL`](http://redis.io/commands/eval)
|
364 |
+
is used as a fall back when needed:
|
365 |
+
|
366 |
+
```php
|
367 |
+
// Define a new script command by extending Predis\Command\ScriptCommand:
|
368 |
+
class ListPushRandomValue extends Predis\Command\ScriptCommand
|
369 |
+
{
|
370 |
+
public function getKeysCount()
|
371 |
+
{
|
372 |
+
return 1;
|
373 |
+
}
|
374 |
+
|
375 |
+
public function getScript()
|
376 |
+
{
|
377 |
+
return <<<LUA
|
378 |
+
math.randomseed(ARGV[1])
|
379 |
+
local rnd = tostring(math.random())
|
380 |
+
redis.call('lpush', KEYS[1], rnd)
|
381 |
+
return rnd
|
382 |
+
LUA;
|
383 |
+
}
|
384 |
+
}
|
385 |
+
|
386 |
+
// Inject the script command in the current profile:
|
387 |
+
$client = new Predis\Client();
|
388 |
+
$client->getProfile()->defineCommand('lpushrand', 'ListPushRandomValue');
|
389 |
+
|
390 |
+
$response = $client->lpushrand('random_values', $seed = mt_rand());
|
391 |
+
```
|
392 |
+
|
393 |
+
|
394 |
+
### Customizable connection backends ###
|
395 |
+
|
396 |
+
Predis can use different connection backends to connect to Redis. Two of them leverage a third party
|
397 |
+
extension such as [phpiredis](https://github.com/nrk/phpiredis) resulting in major performance gains
|
398 |
+
especially when dealing with big multibulk responses. While one is based on PHP streams, the other
|
399 |
+
is based on socket resources provided by `ext-socket`. Both support TCP/IP and UNIX domain sockets:
|
400 |
+
|
401 |
+
```php
|
402 |
+
$client = new Predis\Client('tcp://127.0.0.1', [
|
403 |
+
'connections' => [
|
404 |
+
'tcp' => 'Predis\Connection\PhpiredisStreamConnection', // PHP stream resources
|
405 |
+
'unix' => 'Predis\Connection\PhpiredisSocketConnection', // ext-socket resources
|
406 |
+
],
|
407 |
+
]);
|
408 |
+
```
|
409 |
+
|
410 |
+
Developers can create their own connection classes to support whole new network backends, extend
|
411 |
+
existing classes or provide completely different implementations. Connection classes must implement
|
412 |
+
`Predis\Connection\NodeConnectionInterface` or extend `Predis\Connection\AbstractConnection`:
|
413 |
+
|
414 |
+
```php
|
415 |
+
class MyConnectionClass implements Predis\Connection\NodeConnectionInterface
|
416 |
+
{
|
417 |
+
// Implementation goes here...
|
418 |
+
}
|
419 |
+
|
420 |
+
// Use MyConnectionClass to handle connections for the `tcp` scheme:
|
421 |
+
$client = new Predis\Client('tcp://127.0.0.1', [
|
422 |
+
'connections' => ['tcp' => 'MyConnectionClass'],
|
423 |
+
]);
|
424 |
+
```
|
425 |
+
|
426 |
+
For a more in-depth insight on how to create new connection backends you can refer to the actual
|
427 |
+
implementation of the standard connection classes available in the `Predis\Connection` namespace.
|
428 |
+
|
429 |
+
|
430 |
+
## Development ##
|
431 |
+
|
432 |
+
|
433 |
+
### Reporting bugs and contributing code ###
|
434 |
+
|
435 |
+
Contributions to Predis are highly appreciated either in the form of pull requests for new features,
|
436 |
+
bug fixes, or just bug reports. We only ask you to adhere to a [basic set of rules](CONTRIBUTING.md)
|
437 |
+
before submitting your changes or filing bugs on the issue tracker to make it easier for everyone to
|
438 |
+
stay consistent while working on the project.
|
439 |
+
|
440 |
+
|
441 |
+
### Test suite ###
|
442 |
+
|
443 |
+
__ATTENTION__: Do not ever run the test suite shipped with Predis against instances of Redis running
|
444 |
+
in production environments or containing data you are interested in!
|
445 |
+
|
446 |
+
Predis has a comprehensive test suite covering every aspect of the library. This test suite performs
|
447 |
+
integration tests against a running instance of Redis (>= 2.4.0 is required) to verify the correct
|
448 |
+
behavior of the implementation of each command and automatically skips commands not defined in the
|
449 |
+
specified Redis profile. If you do not have Redis up and running, integration tests can be disabled.
|
450 |
+
By default the test suite is configured to execute integration tests using the profile for Redis 3.2
|
451 |
+
(which is the current stable version of Redis) but can optionally target a Redis instance built from
|
452 |
+
the `unstable` branch by modifying `phpunit.xml` and setting `REDIS_SERVER_VERSION` to `dev` so that
|
453 |
+
the development server profile will be used. You can refer to [the tests README](tests/README.md)
|
454 |
+
for more detailed information about testing Predis.
|
455 |
+
|
456 |
+
Predis uses Travis CI for continuous integration and the history for past and current builds can be
|
457 |
+
found [on its project page](http://travis-ci.org/nrk/predis).
|
458 |
+
|
459 |
+
|
460 |
+
## Other ##
|
461 |
+
|
462 |
+
|
463 |
+
### Project related links ###
|
464 |
+
|
465 |
+
- [Source code](https://github.com/nrk/predis)
|
466 |
+
- [Wiki](https://wiki.github.com/nrk/predis)
|
467 |
+
- [Issue tracker](https://github.com/nrk/predis/issues)
|
468 |
+
- [PEAR channel](http://pear.nrk.io)
|
469 |
+
|
470 |
+
|
471 |
+
### Author ###
|
472 |
+
|
473 |
+
- [Daniele Alessandri](mailto:suppakilla@gmail.com) ([twitter](http://twitter.com/JoL1hAHN))
|
474 |
+
|
475 |
+
|
476 |
+
### License ###
|
477 |
+
|
478 |
+
The code for Predis is distributed under the terms of the MIT license (see [LICENSE](LICENSE)).
|
479 |
+
|
480 |
+
[ico-license]: https://img.shields.io/github/license/nrk/predis.svg?style=flat-square
|
481 |
+
[ico-version-stable]: https://img.shields.io/packagist/v/predis/predis.svg?style=flat-square
|
482 |
+
[ico-version-dev]: https://img.shields.io/packagist/vpre/predis/predis.svg?style=flat-square
|
483 |
+
[ico-downloads-monthly]: https://img.shields.io/packagist/dm/predis/predis.svg?style=flat-square
|
484 |
+
[ico-travis]: https://img.shields.io/travis/nrk/predis.svg?style=flat-square
|
485 |
+
[ico-hhvm]: https://img.shields.io/hhvm/predis/predis.svg?style=flat-square
|
486 |
+
[ico-gitter]: https://img.shields.io/gitter/room/nrk/predis.svg?style=flat-square
|
487 |
+
|
488 |
+
[link-packagist]: https://packagist.org/packages/predis/predis
|
489 |
+
[link-travis]: https://travis-ci.org/nrk/predis
|
490 |
+
[link-downloads]: https://packagist.org/packages/predis/predis/stats
|
491 |
+
[link-hhvm]: http://hhvm.h4cc.de/package/predis/predis
|
492 |
+
[link-gitter]: https://gitter.im/nrk/predis
|
{includes → dependencies/vendor/predis}/predis/VERSION
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/autoload.php
RENAMED
File without changes
|
dependencies/vendor/predis/predis/bin/create-command-test
ADDED
@@ -0,0 +1,275 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/usr/bin/env php
|
2 |
+
<?php
|
3 |
+
|
4 |
+
/*
|
5 |
+
* This file is part of the Predis package.
|
6 |
+
*
|
7 |
+
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
8 |
+
*
|
9 |
+
* For the full copyright and license information, please view the LICENSE
|
10 |
+
* file that was distributed with this source code.
|
11 |
+
*/
|
12 |
+
|
13 |
+
// -------------------------------------------------------------------------- //
|
14 |
+
// This script can be used to automatically generate a file with the scheleton
|
15 |
+
// of a test case to test a Redis command by specifying the name of the class
|
16 |
+
// in the Predis\Command namespace (only classes in this namespace are valid).
|
17 |
+
// For example, to generate a test case for SET (which is represented by the
|
18 |
+
// Predis\Command\StringSet class):
|
19 |
+
//
|
20 |
+
// $ ./bin/generate-command-test --class=StringSet
|
21 |
+
//
|
22 |
+
// Here is a list of optional arguments:
|
23 |
+
//
|
24 |
+
// --realm: each command has its own realm (commands that operate on strings,
|
25 |
+
// lists, sets and such) but while this realm is usually inferred from the name
|
26 |
+
// of the specified class, sometimes it can be useful to override it with a
|
27 |
+
// custom one.
|
28 |
+
//
|
29 |
+
// --output: write the generated test case to the specified path instead of
|
30 |
+
// the default one.
|
31 |
+
//
|
32 |
+
// --overwrite: pre-existing test files are not overwritten unless this option
|
33 |
+
// is explicitly specified.
|
34 |
+
// -------------------------------------------------------------------------- //
|
35 |
+
|
36 |
+
use Predis\Command\CommandInterface;
|
37 |
+
use Predis\Command\PrefixableCommandInterface;
|
38 |
+
|
39 |
+
class CommandTestCaseGenerator
|
40 |
+
{
|
41 |
+
private $options;
|
42 |
+
|
43 |
+
public function __construct(array $options)
|
44 |
+
{
|
45 |
+
if (!isset($options['class'])) {
|
46 |
+
throw new RuntimeException("Missing 'class' option.");
|
47 |
+
}
|
48 |
+
$this->options = $options;
|
49 |
+
}
|
50 |
+
|
51 |
+
public static function fromCommandLine()
|
52 |
+
{
|
53 |
+
$parameters = array(
|
54 |
+
'c:' => 'class:',
|
55 |
+
'r::' => 'realm::',
|
56 |
+
'o::' => 'output::',
|
57 |
+
'x::' => 'overwrite::'
|
58 |
+
);
|
59 |
+
|
60 |
+
$getops = getopt(implode(array_keys($parameters)), $parameters);
|
61 |
+
|
62 |
+
$options = array(
|
63 |
+
'overwrite' => false,
|
64 |
+
'tests' => __DIR__.'/../tests/Predis',
|
65 |
+
);
|
66 |
+
|
67 |
+
foreach ($getops as $option => $value) {
|
68 |
+
switch ($option) {
|
69 |
+
case 'c':
|
70 |
+
case 'class':
|
71 |
+
$options['class'] = $value;
|
72 |
+
break;
|
73 |
+
|
74 |
+
case 'r':
|
75 |
+
case 'realm':
|
76 |
+
$options['realm'] = $value;
|
77 |
+
break;
|
78 |
+
|
79 |
+
case 'o':
|
80 |
+
case 'output':
|
81 |
+
$options['output'] = $value;
|
82 |
+
break;
|
83 |
+
|
84 |
+
case 'x':
|
85 |
+
case 'overwrite':
|
86 |
+
$options['overwrite'] = true;
|
87 |
+
break;
|
88 |
+
}
|
89 |
+
}
|
90 |
+
|
91 |
+
if (!isset($options['class'])) {
|
92 |
+
throw new RuntimeException("Missing 'class' option.");
|
93 |
+
}
|
94 |
+
|
95 |
+
$options['fqn'] = "Predis\\Command\\{$options['class']}";
|
96 |
+
$options['path'] = "Command/{$options['class']}.php";
|
97 |
+
|
98 |
+
$source = __DIR__.'/../src/'.$options['path'];
|
99 |
+
if (!file_exists($source)) {
|
100 |
+
throw new RuntimeException("Cannot find class file for {$options['fqn']} in $source.");
|
101 |
+
}
|
102 |
+
|
103 |
+
if (!isset($options['output'])) {
|
104 |
+
$options['output'] = sprintf("%s/%s", $options['tests'], str_replace('.php', 'Test.php', $options['path']));
|
105 |
+
}
|
106 |
+
|
107 |
+
return new self($options);
|
108 |
+
}
|
109 |
+
|
110 |
+
protected function getTestRealm()
|
111 |
+
{
|
112 |
+
if (isset($this->options['realm'])) {
|
113 |
+
if (!$this->options['realm']) {
|
114 |
+
throw new RuntimeException('Invalid value for realm has been sepcified (empty).');
|
115 |
+
}
|
116 |
+
return $this->options['realm'];
|
117 |
+
}
|
118 |
+
|
119 |
+
$fqnParts = explode('\\', $this->options['fqn']);
|
120 |
+
$class = array_pop($fqnParts);
|
121 |
+
list($realm,) = preg_split('/([[:upper:]][[:lower:]]+)/', $class, 2, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
|
122 |
+
|
123 |
+
return strtolower($realm);
|
124 |
+
}
|
125 |
+
|
126 |
+
public function generate()
|
127 |
+
{
|
128 |
+
$reflection = new ReflectionClass($class = $this->options['fqn']);
|
129 |
+
|
130 |
+
if (!$reflection->isInstantiable()) {
|
131 |
+
throw new RuntimeException("Class $class must be instantiable, abstract classes or interfaces are not allowed.");
|
132 |
+
}
|
133 |
+
if (!$reflection->implementsInterface('Predis\Command\CommandInterface')) {
|
134 |
+
throw new RuntimeException("Class $class must implement Predis\Command\CommandInterface.");
|
135 |
+
}
|
136 |
+
|
137 |
+
/*
|
138 |
+
* @var CommandInterface
|
139 |
+
*/
|
140 |
+
$instance = $reflection->newInstance();
|
141 |
+
|
142 |
+
$buffer = $this->getTestCaseBuffer($instance);
|
143 |
+
|
144 |
+
return $buffer;
|
145 |
+
}
|
146 |
+
|
147 |
+
public function save()
|
148 |
+
{
|
149 |
+
$options = $this->options;
|
150 |
+
if (file_exists($options['output']) && !$options['overwrite']) {
|
151 |
+
throw new RuntimeException("File {$options['output']} already exist. Specify the --overwrite option to overwrite the existing file.");
|
152 |
+
}
|
153 |
+
file_put_contents($options['output'], $this->generate());
|
154 |
+
}
|
155 |
+
|
156 |
+
protected function getTestCaseBuffer(CommandInterface $instance)
|
157 |
+
{
|
158 |
+
$id = $instance->getId();
|
159 |
+
$fqn = get_class($instance);
|
160 |
+
$fqnParts = explode('\\', $fqn);
|
161 |
+
$class = array_pop($fqnParts) . "Test";
|
162 |
+
$realm = $this->getTestRealm();
|
163 |
+
|
164 |
+
$buffer =<<<PHP
|
165 |
+
<?php
|
166 |
+
|
167 |
+
/*
|
168 |
+
* This file is part of the Predis package.
|
169 |
+
*
|
170 |
+
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
171 |
+
*
|
172 |
+
* For the full copyright and license information, please view the LICENSE
|
173 |
+
* file that was distributed with this source code.
|
174 |
+
*/
|
175 |
+
|
176 |
+
namespace Predis\Command;
|
177 |
+
|
178 |
+
/**
|
179 |
+
* @group commands
|
180 |
+
* @group realm-$realm
|
181 |
+
*/
|
182 |
+
class $class extends PredisCommandTestCase
|
183 |
+
{
|
184 |
+
/**
|
185 |
+
* {@inheritdoc}
|
186 |
+
*/
|
187 |
+
protected function getExpectedCommand()
|
188 |
+
{
|
189 |
+
return '$fqn';
|
190 |
+
}
|
191 |
+
|
192 |
+
/**
|
193 |
+
* {@inheritdoc}
|
194 |
+
*/
|
195 |
+
protected function getExpectedId()
|
196 |
+
{
|
197 |
+
return '$id';
|
198 |
+
}
|
199 |
+
|
200 |
+
/**
|
201 |
+
* @group disconnected
|
202 |
+
*/
|
203 |
+
public function testFilterArguments()
|
204 |
+
{
|
205 |
+
\$this->markTestIncomplete('This test has not been implemented yet.');
|
206 |
+
|
207 |
+
\$arguments = array(/* add arguments */);
|
208 |
+
\$expected = array(/* add arguments */);
|
209 |
+
|
210 |
+
\$command = \$this->getCommand();
|
211 |
+
\$command->setArguments(\$arguments);
|
212 |
+
|
213 |
+
\$this->assertSame(\$expected, \$command->getArguments());
|
214 |
+
}
|
215 |
+
|
216 |
+
/**
|
217 |
+
* @group disconnected
|
218 |
+
*/
|
219 |
+
public function testParseResponse()
|
220 |
+
{
|
221 |
+
\$this->markTestIncomplete('This test has not been implemented yet.');
|
222 |
+
|
223 |
+
\$raw = null;
|
224 |
+
\$expected = null;
|
225 |
+
|
226 |
+
\$command = \$this->getCommand();
|
227 |
+
|
228 |
+
\$this->assertSame(\$expected, \$command->parseResponse(\$raw));
|
229 |
+
}
|
230 |
+
|
231 |
+
PHP;
|
232 |
+
|
233 |
+
if ($instance instanceof PrefixableCommandInterface) {
|
234 |
+
$buffer .=<<<PHP
|
235 |
+
|
236 |
+
/**
|
237 |
+
* @group disconnected
|
238 |
+
*/
|
239 |
+
public function testPrefixKeys()
|
240 |
+
{
|
241 |
+
\$this->markTestIncomplete('This test has not been implemented yet.');
|
242 |
+
|
243 |
+
\$arguments = array(/* add arguments */);
|
244 |
+
\$expected = array(/* add arguments */);
|
245 |
+
|
246 |
+
\$command = \$this->getCommandWithArgumentsArray(\$arguments);
|
247 |
+
\$command->prefixKeys('prefix:');
|
248 |
+
|
249 |
+
\$this->assertSame(\$expected, \$command->getArguments());
|
250 |
+
}
|
251 |
+
|
252 |
+
/**
|
253 |
+
* @group disconnected
|
254 |
+
*/
|
255 |
+
public function testPrefixKeysIgnoredOnEmptyArguments()
|
256 |
+
{
|
257 |
+
\$command = \$this->getCommand();
|
258 |
+
\$command->prefixKeys('prefix:');
|
259 |
+
|
260 |
+
\$this->assertSame(array(), \$command->getArguments());
|
261 |
+
}
|
262 |
+
|
263 |
+
PHP;
|
264 |
+
}
|
265 |
+
|
266 |
+
return "$buffer}\n";
|
267 |
+
}
|
268 |
+
}
|
269 |
+
|
270 |
+
// ------------------------------------------------------------------------- //
|
271 |
+
|
272 |
+
require __DIR__.'/../autoload.php';
|
273 |
+
|
274 |
+
$generator = CommandTestCaseGenerator::fromCommandLine();
|
275 |
+
$generator->save();
|
dependencies/vendor/predis/predis/bin/create-pear
ADDED
@@ -0,0 +1,233 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/usr/bin/env php
|
2 |
+
<?php
|
3 |
+
|
4 |
+
/*
|
5 |
+
* This file is part of the Predis package.
|
6 |
+
*
|
7 |
+
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
8 |
+
*
|
9 |
+
* For the full copyright and license information, please view the LICENSE
|
10 |
+
* file that was distributed with this source code.
|
11 |
+
*/
|
12 |
+
|
13 |
+
// -------------------------------------------------------------------------- //
|
14 |
+
// In order to be able to execute this script to create a PEAR package of Predis
|
15 |
+
// the `pear` binary must be available and executable in your $PATH.
|
16 |
+
// The parts used to parse author and version strings are taken from Onion (used
|
17 |
+
// by this library in the past) just to keep on relying on the package.ini file
|
18 |
+
// to simplify things. We might consider to switch to using the PEAR classes
|
19 |
+
// directly in the future.
|
20 |
+
// -------------------------------------------------------------------------- //
|
21 |
+
|
22 |
+
function executeWithBackup($file, $callback)
|
23 |
+
{
|
24 |
+
$exception = null;
|
25 |
+
$backup = "$file.backup";
|
26 |
+
|
27 |
+
copy($file, $backup);
|
28 |
+
|
29 |
+
try {
|
30 |
+
call_user_func($callback, $file);
|
31 |
+
} catch (Exception $exception) {
|
32 |
+
// NOOP
|
33 |
+
}
|
34 |
+
|
35 |
+
unlink($file);
|
36 |
+
rename($backup, $file);
|
37 |
+
|
38 |
+
if ($exception) {
|
39 |
+
throw $exception;
|
40 |
+
}
|
41 |
+
}
|
42 |
+
|
43 |
+
function parseAuthor($string)
|
44 |
+
{
|
45 |
+
$author = array();
|
46 |
+
|
47 |
+
if (preg_match('/^\s*(.+?)\s*(?:"(\S+)"\s*)?<(\S+)>\s*$/x', $string , $regs)) {
|
48 |
+
if (count($regs) == 4) {
|
49 |
+
list($_,$name,$user,$email) = $regs;
|
50 |
+
$author['name'] = $name;
|
51 |
+
$author['user'] = $user;
|
52 |
+
$author['email'] = $email;
|
53 |
+
} elseif (count($regs) == 3) {
|
54 |
+
list($_,$name,$email) = $regs;
|
55 |
+
$author['name'] = $name;
|
56 |
+
$author['email'] = $email;
|
57 |
+
}
|
58 |
+
} else {
|
59 |
+
$author['name'] = $string;
|
60 |
+
}
|
61 |
+
|
62 |
+
return $author;
|
63 |
+
}
|
64 |
+
|
65 |
+
function parseVersion($string)
|
66 |
+
{
|
67 |
+
$version_pattern = '([0-9.]+)';
|
68 |
+
|
69 |
+
if (preg_match("/^\s*$version_pattern\s*\$/x", $string, $regs)) {
|
70 |
+
return array('min' => $regs[1] ?: '0.0.0');
|
71 |
+
} elseif (preg_match("/^\s*[>=]+\s*$version_pattern\s*\$/x", $string, $regs)) {
|
72 |
+
return array('min' => $regs[1] ?: '0.0.0');
|
73 |
+
} elseif (preg_match("/^\s*[<=]+\s*$version_pattern\s*\$/x", $string, $regs)) {
|
74 |
+
return array('max' => $regs[1]);
|
75 |
+
} elseif (preg_match("/^\s*$version_pattern\s*<=>\s*$version_pattern\s*\$/x", $string, $regs)) {
|
76 |
+
return array(
|
77 |
+
'min' => $regs[1] ?: '0.0.0',
|
78 |
+
'max' => $regs[2],
|
79 |
+
);
|
80 |
+
}
|
81 |
+
|
82 |
+
return null;
|
83 |
+
}
|
84 |
+
|
85 |
+
function addRolePath($pkg, $path, $role)
|
86 |
+
{
|
87 |
+
if (is_dir($path)) {
|
88 |
+
$dirRoot = new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS);
|
89 |
+
$dirTree = new RecursiveIteratorIterator($dirRoot, RecursiveIteratorIterator::CHILD_FIRST);
|
90 |
+
|
91 |
+
foreach ($dirTree as $fileinfo) {
|
92 |
+
if ($fileinfo->isFile()) {
|
93 |
+
addPackageFile($pkg, $fileinfo, $role, $path);
|
94 |
+
}
|
95 |
+
}
|
96 |
+
} else {
|
97 |
+
foreach (glob($path) as $filename) {
|
98 |
+
addPackageFile($pkg, new SplFileInfo($filename), $role);
|
99 |
+
}
|
100 |
+
}
|
101 |
+
}
|
102 |
+
|
103 |
+
function addPackageFile($pkg, $fileinfo, $role, $baseDir = '')
|
104 |
+
{
|
105 |
+
$fileNode = $pkg->contents->dir->addChild('file');
|
106 |
+
$fileNode->addAttribute('name', $filepath = $fileinfo->getPathname());
|
107 |
+
$fileNode->addAttribute('role', $role);
|
108 |
+
$fileNode->addAttribute('md5sum', md5_file($filepath));
|
109 |
+
|
110 |
+
$installNode = $pkg->phprelease->filelist->addChild('install');
|
111 |
+
$installNode->addAttribute('name', $filepath);
|
112 |
+
$installNode->addAttribute('as', !$baseDir ? basename($filepath) : substr($filepath, strlen($baseDir) + 1));
|
113 |
+
}
|
114 |
+
|
115 |
+
function generatePackageXml($packageINI)
|
116 |
+
{
|
117 |
+
$XML = <<<XML
|
118 |
+
<?xml version="1.0"?>
|
119 |
+
<package packagerversion="1.4.10" version="2.0"
|
120 |
+
xmlns="http://pear.php.net/dtd/package-2.0"
|
121 |
+
xmlns:tasks="http://pear.php.net/dtd/tasks-1.0"
|
122 |
+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
123 |
+
xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0
|
124 |
+
http://pear.php.net/dtd/tasks-1.0.xsd
|
125 |
+
http://pear.php.net/dtd/package-2.0
|
126 |
+
http://pear.php.net/dtd/package-2.0.xsd" />
|
127 |
+
XML;
|
128 |
+
|
129 |
+
$cfg = parse_ini_file($packageINI, true);
|
130 |
+
$pkg = new SimpleXMLElement($XML);
|
131 |
+
|
132 |
+
$pkg->name = $cfg['package']['name'];
|
133 |
+
$pkg->channel = $cfg['package']['channel'];
|
134 |
+
$pkg->summary = $cfg['package']['desc'];
|
135 |
+
$pkg->description = $cfg['package']['desc'];
|
136 |
+
|
137 |
+
$author = parseAuthor($cfg['package']['author']);
|
138 |
+
$pkg->addChild('lead');
|
139 |
+
$pkg->lead->name = $author['name'];
|
140 |
+
$pkg->lead->user = $author['user'];
|
141 |
+
$pkg->lead->email = $author['email'];
|
142 |
+
$pkg->lead->active = 'yes';
|
143 |
+
|
144 |
+
$datetime = new DateTime('now');
|
145 |
+
$pkg->date = $datetime->format('Y-m-d');
|
146 |
+
$pkg->time = $datetime->format('H:i:s');
|
147 |
+
|
148 |
+
$pkg->addChild('version');
|
149 |
+
$pkg->version->release = $cfg['package']['version'];
|
150 |
+
$pkg->version->api = $cfg['package']['version'];
|
151 |
+
|
152 |
+
$pkg->addChild('stability');
|
153 |
+
$pkg->stability->release = $cfg['package']['stability'];
|
154 |
+
$pkg->stability->api = $cfg['package']['stability'];
|
155 |
+
|
156 |
+
$pkg->license = $cfg['package']['license'];
|
157 |
+
$pkg->notes = '-';
|
158 |
+
|
159 |
+
$pkg->addChild('contents')->addChild('dir')->addAttribute('name', '/');
|
160 |
+
|
161 |
+
$pkg->addChild('dependencies')->addChild('required');
|
162 |
+
foreach ($cfg['require'] as $required => $version) {
|
163 |
+
$version = parseVersion($version);
|
164 |
+
$pkg->dependencies->required->addChild($required);
|
165 |
+
|
166 |
+
if (isset($version['min'])) {
|
167 |
+
$pkg->dependencies->required->$required->min = $version['min'];
|
168 |
+
}
|
169 |
+
if (isset($version['max'])) {
|
170 |
+
$pkg->dependencies->required->$required->min = $version['max'];
|
171 |
+
}
|
172 |
+
}
|
173 |
+
|
174 |
+
$pkg->addChild('phprelease')->addChild('filelist');
|
175 |
+
|
176 |
+
$pathToRole = array(
|
177 |
+
'doc' => 'doc', 'docs' => 'doc', 'examples' => 'doc',
|
178 |
+
'lib' => 'php', 'src' => 'php',
|
179 |
+
'test' => 'test', 'tests' => 'test',
|
180 |
+
);
|
181 |
+
|
182 |
+
foreach (array_merge($pathToRole, $cfg['roles'] ?: array()) as $path => $role) {
|
183 |
+
addRolePath($pkg, $path, $role);
|
184 |
+
}
|
185 |
+
|
186 |
+
return $pkg;
|
187 |
+
}
|
188 |
+
|
189 |
+
function rewritePackageInstallAs($pkg)
|
190 |
+
{
|
191 |
+
foreach ($pkg->phprelease->filelist->install as $file) {
|
192 |
+
if (preg_match('/^src\//', $file['name'])) {
|
193 |
+
$file['as'] = "Predis/{$file['as']}";
|
194 |
+
}
|
195 |
+
}
|
196 |
+
}
|
197 |
+
|
198 |
+
function savePackageXml($xml)
|
199 |
+
{
|
200 |
+
$dom = new DOMDocument("1.0");
|
201 |
+
$dom->preserveWhiteSpace = false;
|
202 |
+
$dom->formatOutput = true;
|
203 |
+
$dom->loadXML($xml->asXML());
|
204 |
+
|
205 |
+
file_put_contents('package.xml', $dom->saveXML());
|
206 |
+
}
|
207 |
+
|
208 |
+
function buildPackage()
|
209 |
+
{
|
210 |
+
passthru('pear -q package && rm package.xml');
|
211 |
+
}
|
212 |
+
|
213 |
+
function modifyPhpunitXml($file)
|
214 |
+
{
|
215 |
+
$cfg = new SimpleXMLElement($file, null, true);
|
216 |
+
|
217 |
+
$cfg[0]['bootstrap'] = str_replace('tests/', '', $cfg[0]['bootstrap']);
|
218 |
+
$cfg->testsuites->testsuite->directory = str_replace('tests/', '', $cfg->testsuites->testsuite->directory);
|
219 |
+
|
220 |
+
$cfg->saveXml($file);
|
221 |
+
}
|
222 |
+
|
223 |
+
// -------------------------------------------------------------------------- //
|
224 |
+
|
225 |
+
executeWithBackup(__DIR__.'/../phpunit.xml.dist', function ($file) {
|
226 |
+
modifyPhpunitXml($file);
|
227 |
+
|
228 |
+
$pkg = generatePackageXml('package.ini');
|
229 |
+
rewritePackageInstallAs($pkg);
|
230 |
+
savePackageXml($pkg);
|
231 |
+
|
232 |
+
buildPackage();
|
233 |
+
});
|
dependencies/vendor/predis/predis/bin/create-phar
ADDED
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/usr/bin/env php
|
2 |
+
<?php
|
3 |
+
|
4 |
+
/*
|
5 |
+
* This file is part of the Predis package.
|
6 |
+
*
|
7 |
+
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
8 |
+
*
|
9 |
+
* For the full copyright and license information, please view the LICENSE
|
10 |
+
* file that was distributed with this source code.
|
11 |
+
*/
|
12 |
+
|
13 |
+
// -------------------------------------------------------------------------- //
|
14 |
+
// In order to be able to execute this script to create a Phar archive of Predis,
|
15 |
+
// the Phar module must be loaded and the "phar.readonly" directive php.ini must
|
16 |
+
// be set to "off". You can change the values in the $options array to customize
|
17 |
+
// the creation of the Phar archive to better suit your needs.
|
18 |
+
// -------------------------------------------------------------------------- //
|
19 |
+
|
20 |
+
$options = array(
|
21 |
+
'name' => 'predis',
|
22 |
+
'project_path' => __DIR__ . '/../src',
|
23 |
+
'compression' => Phar::NONE,
|
24 |
+
'append_version' => true,
|
25 |
+
);
|
26 |
+
|
27 |
+
function getPharFilename($options)
|
28 |
+
{
|
29 |
+
$filename = $options['name'];
|
30 |
+
|
31 |
+
// NOTE: do not consider "append_version" with Phar compression do to a bug in
|
32 |
+
// Phar::compress() when renaming phar archives containing dots in their name.
|
33 |
+
if ($options['append_version'] && $options['compression'] === Phar::NONE) {
|
34 |
+
$versionFile = @fopen(__DIR__ . '/../VERSION', 'r');
|
35 |
+
|
36 |
+
if ($versionFile === false) {
|
37 |
+
throw new Exception("Could not locate the VERSION file.");
|
38 |
+
}
|
39 |
+
|
40 |
+
$version = trim(fgets($versionFile));
|
41 |
+
fclose($versionFile);
|
42 |
+
$filename .= "_$version";
|
43 |
+
}
|
44 |
+
|
45 |
+
return "$filename.phar";
|
46 |
+
}
|
47 |
+
|
48 |
+
function getPharStub($options)
|
49 |
+
{
|
50 |
+
return <<<EOSTUB
|
51 |
+
<?php
|
52 |
+
Phar::mapPhar('predis.phar');
|
53 |
+
spl_autoload_register(function (\$class) {
|
54 |
+
if (strpos(\$class, 'Predis\\\\') === 0) {
|
55 |
+
\$file = 'phar://predis.phar/'.strtr(substr(\$class, 7), '\\\', '/').'.php';
|
56 |
+
if (file_exists(\$file)) {
|
57 |
+
require \$file;
|
58 |
+
return true;
|
59 |
+
}
|
60 |
+
}
|
61 |
+
});
|
62 |
+
__HALT_COMPILER();
|
63 |
+
EOSTUB;
|
64 |
+
}
|
65 |
+
|
66 |
+
// -------------------------------------------------------------------------- //
|
67 |
+
|
68 |
+
$phar = new Phar(getPharFilename($options));
|
69 |
+
$phar->compress($options['compression']);
|
70 |
+
$phar->setStub(getPharStub($options));
|
71 |
+
$phar->buildFromDirectory($options['project_path']);
|
dependencies/vendor/predis/predis/bin/create-single-file
ADDED
@@ -0,0 +1,662 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/usr/bin/env php
|
2 |
+
<?php
|
3 |
+
|
4 |
+
/*
|
5 |
+
* This file is part of the Predis package.
|
6 |
+
*
|
7 |
+
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
8 |
+
*
|
9 |
+
* For the full copyright and license information, please view the LICENSE
|
10 |
+
* file that was distributed with this source code.
|
11 |
+
*/
|
12 |
+
|
13 |
+
// -------------------------------------------------------------------------- //
|
14 |
+
// This script can be used to automatically glue all the .php files of Predis
|
15 |
+
// into a single monolithic script file that can be used without an autoloader,
|
16 |
+
// just like the other previous versions of the library.
|
17 |
+
//
|
18 |
+
// Much of its complexity is due to the fact that we cannot simply join PHP
|
19 |
+
// files, but namespaces and classes definitions must follow a precise order
|
20 |
+
// when dealing with subclassing and inheritance.
|
21 |
+
//
|
22 |
+
// The current implementation is pretty naïve, but it should do for now.
|
23 |
+
// -------------------------------------------------------------------------- //
|
24 |
+
|
25 |
+
class CommandLine
|
26 |
+
{
|
27 |
+
public static function getOptions()
|
28 |
+
{
|
29 |
+
$parameters = array(
|
30 |
+
's:' => 'source:',
|
31 |
+
'o:' => 'output:',
|
32 |
+
'e:' => 'exclude:',
|
33 |
+
'E:' => 'exclude-classes:',
|
34 |
+
);
|
35 |
+
|
36 |
+
$getops = getopt(implode(array_keys($parameters)), $parameters);
|
37 |
+
|
38 |
+
$options = array(
|
39 |
+
'source' => __DIR__ . "/../src",
|
40 |
+
'output' => PredisFile::NS_ROOT . '.php',
|
41 |
+
'exclude' => array(),
|
42 |
+
);
|
43 |
+
|
44 |
+
foreach ($getops as $option => $value) {
|
45 |
+
switch ($option) {
|
46 |
+
case 's':
|
47 |
+
case 'source':
|
48 |
+
$options['source'] = $value;
|
49 |
+
break;
|
50 |
+
|
51 |
+
case 'o':
|
52 |
+
case 'output':
|
53 |
+
$options['output'] = $value;
|
54 |
+
break;
|
55 |
+
|
56 |
+
case 'E':
|
57 |
+
case 'exclude-classes':
|
58 |
+
$options['exclude'] = @file($value, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) ?: $value;
|
59 |
+
break;
|
60 |
+
|
61 |
+
case 'e':
|
62 |
+
case 'exclude':
|
63 |
+
$options['exclude'] = is_array($value) ? $value : array($value);
|
64 |
+
break;
|
65 |
+
}
|
66 |
+
}
|
67 |
+
|
68 |
+
return $options;
|
69 |
+
}
|
70 |
+
}
|
71 |
+
|
72 |
+
class PredisFile
|
73 |
+
{
|
74 |
+
const NS_ROOT = 'Predis';
|
75 |
+
|
76 |
+
private $namespaces;
|
77 |
+
|
78 |
+
public function __construct()
|
79 |
+
{
|
80 |
+
$this->namespaces = array();
|
81 |
+
}
|
82 |
+
|
83 |
+
public static function from($libraryPath, array $exclude = array())
|
84 |
+
{
|
85 |
+
$predisFile = new PredisFile();
|
86 |
+
$libIterator = new RecursiveDirectoryIterator($libraryPath);
|
87 |
+
|
88 |
+
foreach (new RecursiveIteratorIterator($libIterator) as $classFile)
|
89 |
+
{
|
90 |
+
if (!$classFile->isFile()) {
|
91 |
+
continue;
|
92 |
+
}
|
93 |
+
|
94 |
+
$namespace = self::NS_ROOT.strtr(str_replace($libraryPath, '', $classFile->getPath()), '/', '\\');
|
95 |
+
|
96 |
+
if (in_array(sprintf('%s\\%s', $namespace, $classFile->getBasename('.php')), $exclude)) {
|
97 |
+
continue;
|
98 |
+
}
|
99 |
+
|
100 |
+
$phpNamespace = $predisFile->getNamespace($namespace);
|
101 |
+
|
102 |
+
if ($phpNamespace === false) {
|
103 |
+
$phpNamespace = new PhpNamespace($namespace);
|
104 |
+
$predisFile->addNamespace($phpNamespace);
|
105 |
+
}
|
106 |
+
|
107 |
+
$phpClass = new PhpClass($phpNamespace, $classFile);
|
108 |
+
}
|
109 |
+
|
110 |
+
return $predisFile;
|
111 |
+
}
|
112 |
+
|
113 |
+
public function addNamespace(PhpNamespace $namespace)
|
114 |
+
{
|
115 |
+
if (isset($this->namespaces[(string)$namespace])) {
|
116 |
+
throw new InvalidArgumentException("Duplicated namespace");
|
117 |
+
}
|
118 |
+
$this->namespaces[(string)$namespace] = $namespace;
|
119 |
+
}
|
120 |
+
|
121 |
+
public function getNamespaces()
|
122 |
+
{
|
123 |
+
return $this->namespaces;
|
124 |
+
}
|
125 |
+
|
126 |
+
public function getNamespace($namespace)
|
127 |
+
{
|
128 |
+
if (!isset($this->namespaces[$namespace])) {
|
129 |
+
return false;
|
130 |
+
}
|
131 |
+
|
132 |
+
return $this->namespaces[$namespace];
|
133 |
+
}
|
134 |
+
|
135 |
+
public function getClassByFQN($classFqn)
|
136 |
+
{
|
137 |
+
if (($nsLastPos = strrpos($classFqn, '\\')) !== false) {
|
138 |
+
$namespace = $this->getNamespace(substr($classFqn, 0, $nsLastPos));
|
139 |
+
if ($namespace === false) {
|
140 |
+
return null;
|
141 |
+
}
|
142 |
+
$className = substr($classFqn, $nsLastPos + 1);
|
143 |
+
|
144 |
+
return $namespace->getClass($className);
|
145 |
+
}
|
146 |
+
|
147 |
+
return null;
|
148 |
+
}
|
149 |
+
|
150 |
+
private function calculateDependencyScores(&$classes, $fqn)
|
151 |
+
{
|
152 |
+
if (!isset($classes[$fqn])) {
|
153 |
+
$classes[$fqn] = 0;
|
154 |
+
}
|
155 |
+
|
156 |
+
$classes[$fqn] += 1;
|
157 |
+
|
158 |
+
if (($phpClass = $this->getClassByFQN($fqn)) === null) {
|
159 |
+
throw new RuntimeException(
|
160 |
+
"Cannot found the class $fqn which is required by other subclasses. Are you missing a file?"
|
161 |
+
);
|
162 |
+
}
|
163 |
+
|
164 |
+
foreach ($phpClass->getDependencies() as $fqn) {
|
165 |
+
$this->calculateDependencyScores($classes, $fqn);
|
166 |
+
}
|
167 |
+
}
|
168 |
+
|
169 |
+
private function getDependencyScores()
|
170 |
+
{
|
171 |
+
$classes = array();
|
172 |
+
|
173 |
+
foreach ($this->getNamespaces() as $phpNamespace) {
|
174 |
+
foreach ($phpNamespace->getClasses() as $phpClass) {
|
175 |
+
$this->calculateDependencyScores($classes, $phpClass->getFQN());
|
176 |
+
}
|
177 |
+
}
|
178 |
+
|
179 |
+
return $classes;
|
180 |
+
}
|
181 |
+
|
182 |
+
private function getOrderedNamespaces($dependencyScores)
|
183 |
+
{
|
184 |
+
$namespaces = array_fill_keys(array_unique(
|
185 |
+
array_map(
|
186 |
+
function ($fqn) { return PhpNamespace::extractName($fqn); },
|
187 |
+
array_keys($dependencyScores)
|
188 |
+
)
|
189 |
+
), 0);
|
190 |
+
|
191 |
+
foreach ($dependencyScores as $classFqn => $score) {
|
192 |
+
$namespaces[PhpNamespace::extractName($classFqn)] += $score;
|
193 |
+
}
|
194 |
+
|
195 |
+
arsort($namespaces);
|
196 |
+
|
197 |
+
return array_keys($namespaces);
|
198 |
+
}
|
199 |
+
|
200 |
+
private function getOrderedClasses(PhpNamespace $phpNamespace, $classes)
|
201 |
+
{
|
202 |
+
$nsClassesFQNs = array_map(function ($cl) { return $cl->getFQN(); }, $phpNamespace->getClasses());
|
203 |
+
$nsOrderedClasses = array();
|
204 |
+
|
205 |
+
foreach ($nsClassesFQNs as $nsClassFQN) {
|
206 |
+
$nsOrderedClasses[$nsClassFQN] = $classes[$nsClassFQN];
|
207 |
+
}
|
208 |
+
|
209 |
+
arsort($nsOrderedClasses);
|
210 |
+
|
211 |
+
return array_keys($nsOrderedClasses);
|
212 |
+
}
|
213 |
+
|
214 |
+
public function getPhpCode()
|
215 |
+
{
|
216 |
+
$buffer = array("<?php\n\n", PhpClass::LICENSE_HEADER, "\n\n");
|
217 |
+
$classes = $this->getDependencyScores();
|
218 |
+
$namespaces = $this->getOrderedNamespaces($classes);
|
219 |
+
|
220 |
+
foreach ($namespaces as $namespace) {
|
221 |
+
$phpNamespace = $this->getNamespace($namespace);
|
222 |
+
|
223 |
+
// generate namespace directive
|
224 |
+
$buffer[] = $phpNamespace->getPhpCode();
|
225 |
+
$buffer[] = "\n";
|
226 |
+
|
227 |
+
// generate use directives
|
228 |
+
$useDirectives = $phpNamespace->getUseDirectives();
|
229 |
+
if (count($useDirectives) > 0) {
|
230 |
+
$buffer[] = $useDirectives->getPhpCode();
|
231 |
+
$buffer[] = "\n";
|
232 |
+
}
|
233 |
+
|
234 |
+
// generate classes bodies
|
235 |
+
$nsClasses = $this->getOrderedClasses($phpNamespace, $classes);
|
236 |
+
foreach ($nsClasses as $classFQN) {
|
237 |
+
$buffer[] = $this->getClassByFQN($classFQN)->getPhpCode();
|
238 |
+
$buffer[] = "\n\n";
|
239 |
+
}
|
240 |
+
|
241 |
+
$buffer[] = "/* " . str_repeat("-", 75) . " */";
|
242 |
+
$buffer[] = "\n\n";
|
243 |
+
}
|
244 |
+
|
245 |
+
return implode($buffer);
|
246 |
+
}
|
247 |
+
|
248 |
+
public function saveTo($outputFile)
|
249 |
+
{
|
250 |
+
// TODO: add more sanity checks
|
251 |
+
if ($outputFile === null || $outputFile === '') {
|
252 |
+
throw new InvalidArgumentException('You must specify a valid output file');
|
253 |
+
}
|
254 |
+
file_put_contents($outputFile, $this->getPhpCode());
|
255 |
+
}
|
256 |
+
}
|
257 |
+
|
258 |
+
class PhpNamespace implements IteratorAggregate
|
259 |
+
{
|
260 |
+
private $namespace;
|
261 |
+
private $classes;
|
262 |
+
|
263 |
+
public function __construct($namespace)
|
264 |
+
{
|
265 |
+
$this->namespace = $namespace;
|
266 |
+
$this->classes = array();
|
267 |
+
$this->useDirectives = new PhpUseDirectives($this);
|
268 |
+
}
|
269 |
+
|
270 |
+
public static function extractName($fqn)
|
271 |
+
{
|
272 |
+
$nsSepLast = strrpos($fqn, '\\');
|
273 |
+
if ($nsSepLast === false) {
|
274 |
+
return $fqn;
|
275 |
+
}
|
276 |
+
$ns = substr($fqn, 0, $nsSepLast);
|
277 |
+
|
278 |
+
return $ns !== '' ? $ns : null;
|
279 |
+
}
|
280 |
+
|
281 |
+
public function addClass(PhpClass $class)
|
282 |
+
{
|
283 |
+
$this->classes[$class->getName()] = $class;
|
284 |
+
}
|
285 |
+
|
286 |
+
public function getClass($className)
|
287 |
+
{
|
288 |
+
if (isset($this->classes[$className])) {
|
289 |
+
return $this->classes[$className];
|
290 |
+
}
|
291 |
+
}
|
292 |
+
|
293 |
+
public function getClasses()
|
294 |
+
{
|
295 |
+
return array_values($this->classes);
|
296 |
+
}
|
297 |
+
|
298 |
+
public function getIterator()
|
299 |
+
{
|
300 |
+
return new \ArrayIterator($this->getClasses());
|
301 |
+
}
|
302 |
+
|
303 |
+
public function getUseDirectives()
|
304 |
+
{
|
305 |
+
return $this->useDirectives;
|
306 |
+
}
|
307 |
+
|
308 |
+
public function getPhpCode()
|
309 |
+
{
|
310 |
+
return "namespace $this->namespace;\n";
|
311 |
+
}
|
312 |
+
|
313 |
+
public function __toString()
|
314 |
+
{
|
315 |
+
return $this->namespace;
|
316 |
+
}
|
317 |
+
}
|
318 |
+
|
319 |
+
class PhpUseDirectives implements Countable, IteratorAggregate
|
320 |
+
{
|
321 |
+
private $use;
|
322 |
+
private $aliases;
|
323 |
+
private $reverseAliases;
|
324 |
+
private $namespace;
|
325 |
+
|
326 |
+
public function __construct(PhpNamespace $namespace)
|
327 |
+
{
|
328 |
+
$this->namespace = $namespace;
|
329 |
+
$this->use = array();
|
330 |
+
$this->aliases = array();
|
331 |
+
$this->reverseAliases = array();
|
332 |
+
}
|
333 |
+
|
334 |
+
public function add($use, $as = null)
|
335 |
+
{
|
336 |
+
if (in_array($use, $this->use)) {
|
337 |
+
return;
|
338 |
+
}
|
339 |
+
|
340 |
+
$rename = null;
|
341 |
+
$this->use[] = $use;
|
342 |
+
$aliasedClassName = $as ?: PhpClass::extractName($use);
|
343 |
+
|
344 |
+
if (isset($this->aliases[$aliasedClassName])) {
|
345 |
+
$parentNs = $this->getParentNamespace();
|
346 |
+
|
347 |
+
if ($parentNs && false !== $pos = strrpos($parentNs, '\\')) {
|
348 |
+
$parentNs = substr($parentNs, $pos);
|
349 |
+
}
|
350 |
+
|
351 |
+
$newAlias = "{$parentNs}_{$aliasedClassName}";
|
352 |
+
$rename = (object) array(
|
353 |
+
'namespace' => $this->namespace,
|
354 |
+
'from' => $aliasedClassName,
|
355 |
+
'to' => $newAlias,
|
356 |
+
);
|
357 |
+
|
358 |
+
$this->aliases[$newAlias] = $use;
|
359 |
+
$as = $newAlias;
|
360 |
+
} else {
|
361 |
+
$this->aliases[$aliasedClassName] = $use;
|
362 |
+
}
|
363 |
+
|
364 |
+
if ($as !== null) {
|
365 |
+
$this->reverseAliases[$use] = $as;
|
366 |
+
}
|
367 |
+
|
368 |
+
return $rename;
|
369 |
+
}
|
370 |
+
|
371 |
+
public function getList()
|
372 |
+
{
|
373 |
+
return $this->use;
|
374 |
+
}
|
375 |
+
|
376 |
+
public function getIterator()
|
377 |
+
{
|
378 |
+
return new \ArrayIterator($this->getList());
|
379 |
+
}
|
380 |
+
|
381 |
+
public function getPhpCode()
|
382 |
+
{
|
383 |
+
$reverseAliases = $this->reverseAliases;
|
384 |
+
|
385 |
+
$reducer = function ($str, $use) use ($reverseAliases) {
|
386 |
+
if (isset($reverseAliases[$use])) {
|
387 |
+
return $str .= "use $use as {$reverseAliases[$use]};\n";
|
388 |
+
} else {
|
389 |
+
return $str .= "use $use;\n";
|
390 |
+
}
|
391 |
+
};
|
392 |
+
|
393 |
+
return array_reduce($this->getList(), $reducer, '');
|
394 |
+
}
|
395 |
+
|
396 |
+
public function getNamespace()
|
397 |
+
{
|
398 |
+
return $this->namespace;
|
399 |
+
}
|
400 |
+
|
401 |
+
public function getParentNamespace()
|
402 |
+
{
|
403 |
+
if (false !== $pos = strrpos($this->namespace, '\\')) {
|
404 |
+
return substr($this->namespace, 0, $pos);
|
405 |
+
}
|
406 |
+
|
407 |
+
return '';
|
408 |
+
}
|
409 |
+
|
410 |
+
public function getFQN($className)
|
411 |
+
{
|
412 |
+
if (($nsSepFirst = strpos($className, '\\')) === false) {
|
413 |
+
if (isset($this->aliases[$className])) {
|
414 |
+
return $this->aliases[$className];
|
415 |
+
}
|
416 |
+
|
417 |
+
return (string)$this->getNamespace() . "\\$className";
|
418 |
+
}
|
419 |
+
|
420 |
+
if ($nsSepFirst != 0) {
|
421 |
+
throw new InvalidArgumentException("Partially qualified names are not supported");
|
422 |
+
}
|
423 |
+
|
424 |
+
return $className;
|
425 |
+
}
|
426 |
+
|
427 |
+
public function count()
|
428 |
+
{
|
429 |
+
return count($this->use);
|
430 |
+
}
|
431 |
+
}
|
432 |
+
|
433 |
+
class PhpClass
|
434 |
+
{
|
435 |
+
const LICENSE_HEADER = <<<LICENSE
|
436 |
+
/*
|
437 |
+
* This file is part of the Predis package.
|
438 |
+
*
|
439 |
+
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
440 |
+
*
|
441 |
+
* For the full copyright and license information, please view the LICENSE
|
442 |
+
* file that was distributed with this source code.
|
443 |
+
*/
|
444 |
+
LICENSE;
|
445 |
+
|
446 |
+
private $namespace;
|
447 |
+
private $file;
|
448 |
+
private $body;
|
449 |
+
private $implements;
|
450 |
+
private $extends;
|
451 |
+
private $name;
|
452 |
+
|
453 |
+
public function __construct(PhpNamespace $namespace, SplFileInfo $classFile)
|
454 |
+
{
|
455 |
+
$this->namespace = $namespace;
|
456 |
+
$this->file = $classFile;
|
457 |
+
$this->implements = array();
|
458 |
+
$this->extends = array();
|
459 |
+
|
460 |
+
$this->extractData();
|
461 |
+
$namespace->addClass($this);
|
462 |
+
}
|
463 |
+
|
464 |
+
public static function extractName($fqn)
|
465 |
+
{
|
466 |
+
$nsSepLast = strrpos($fqn, '\\');
|
467 |
+
if ($nsSepLast === false) {
|
468 |
+
return $fqn;
|
469 |
+
}
|
470 |
+
|
471 |
+
return substr($fqn, $nsSepLast + 1);
|
472 |
+
}
|
473 |
+
|
474 |
+
private function extractData()
|
475 |
+
{
|
476 |
+
$renames = array();
|
477 |
+
$useDirectives = $this->getNamespace()->getUseDirectives();
|
478 |
+
|
479 |
+
$useExtractor = function ($m) use ($useDirectives, &$renames) {
|
480 |
+
array_shift($m);
|
481 |
+
|
482 |
+
if (isset($m[1])) {
|
483 |
+
$m[1] = str_replace(" as ", '', $m[1]);
|
484 |
+
}
|
485 |
+
|
486 |
+
if ($rename = call_user_func_array(array($useDirectives, 'add'), $m)) {
|
487 |
+
$renames[] = $rename;
|
488 |
+
}
|
489 |
+
};
|
490 |
+
|
491 |
+
$classBuffer = stream_get_contents(fopen($this->getFile()->getPathname(), 'r'));
|
492 |
+
|
493 |
+
$classBuffer = str_replace(self::LICENSE_HEADER, '', $classBuffer);
|
494 |
+
|
495 |
+
$classBuffer = preg_replace('/<\?php\s?\\n\s?/', '', $classBuffer);
|
496 |
+
$classBuffer = preg_replace('/\s?\?>\n?/ms', '', $classBuffer);
|
497 |
+
$classBuffer = preg_replace('/namespace\s+[\w\d_\\\\]+;\s?/', '', $classBuffer);
|
498 |
+
$classBuffer = preg_replace_callback('/use\s+([\w\d_\\\\]+)(\s+as\s+.*)?;\s?\n?/', $useExtractor, $classBuffer);
|
499 |
+
|
500 |
+
foreach ($renames as $rename) {
|
501 |
+
$classBuffer = str_replace($rename->from, $rename->to, $classBuffer);
|
502 |
+
}
|
503 |
+
|
504 |
+
$this->body = trim($classBuffer);
|
505 |
+
|
506 |
+
$this->extractHierarchy();
|
507 |
+
}
|
508 |
+
|
509 |
+
private function extractHierarchy()
|
510 |
+
{
|
511 |
+
$implements = array();
|
512 |
+
$extends = array();
|
513 |
+
|
514 |
+
$extractor = function ($iterator, $callback) {
|
515 |
+
$className = '';
|
516 |
+
$iterator->seek($iterator->key() + 1);
|
517 |
+
|
518 |
+
while ($iterator->valid()) {
|
519 |
+
$token = $iterator->current();
|
520 |
+
|
521 |
+
if (is_string($token)) {
|
522 |
+
if (preg_match('/\s?,\s?/', $token)) {
|
523 |
+
$callback(trim($className));
|
524 |
+
$className = '';
|
525 |
+
} else if ($token == '{') {
|
526 |
+
$callback(trim($className));
|
527 |
+
return;
|
528 |
+
}
|
529 |
+
}
|
530 |
+
|
531 |
+
switch ($token[0]) {
|
532 |
+
case T_NS_SEPARATOR:
|
533 |
+
$className .= '\\';
|
534 |
+
break;
|
535 |
+
|
536 |
+
case T_STRING:
|
537 |
+
$className .= $token[1];
|
538 |
+
break;
|
539 |
+
|
540 |
+
case T_IMPLEMENTS:
|
541 |
+
case T_EXTENDS:
|
542 |
+
$callback(trim($className));
|
543 |
+
$iterator->seek($iterator->key() - 1);
|
544 |
+
return;
|
545 |
+
}
|
546 |
+
|
547 |
+
$iterator->next();
|
548 |
+
}
|
549 |
+
};
|
550 |
+
|
551 |
+
$tokens = token_get_all("<?php\n" . trim($this->getPhpCode()));
|
552 |
+
$iterator = new ArrayIterator($tokens);
|
553 |
+
|
554 |
+
while ($iterator->valid()) {
|
555 |
+
$token = $iterator->current();
|
556 |
+
if (is_string($token)) {
|
557 |
+
$iterator->next();
|
558 |
+
continue;
|
559 |
+
}
|
560 |
+
|
561 |
+
switch ($token[0]) {
|
562 |
+
case T_CLASS:
|
563 |
+
case T_INTERFACE:
|
564 |
+
$iterator->seek($iterator->key() + 2);
|
565 |
+
$tk = $iterator->current();
|
566 |
+
$this->name = $tk[1];
|
567 |
+
break;
|
568 |
+
|
569 |
+
case T_IMPLEMENTS:
|
570 |
+
$extractor($iterator, function ($fqn) use (&$implements) {
|
571 |
+
$implements[] = $fqn;
|
572 |
+
});
|
573 |
+
break;
|
574 |
+
|
575 |
+
case T_EXTENDS:
|
576 |
+
$extractor($iterator, function ($fqn) use (&$extends) {
|
577 |
+
$extends[] = $fqn;
|
578 |
+
});
|
579 |
+
break;
|
580 |
+
}
|
581 |
+
|
582 |
+
$iterator->next();
|
583 |
+
}
|
584 |
+
|
585 |
+
$this->implements = $this->guessFQN($implements);
|
586 |
+
$this->extends = $this->guessFQN($extends);
|
587 |
+
}
|
588 |
+
|
589 |
+
public function guessFQN($classes)
|
590 |
+
{
|
591 |
+
$useDirectives = $this->getNamespace()->getUseDirectives();
|
592 |
+
return array_map(array($useDirectives, 'getFQN'), $classes);
|
593 |
+
}
|
594 |
+
|
595 |
+
public function getImplementedInterfaces($all = false)
|
596 |
+
{
|
597 |
+
if ($all) {
|
598 |
+
return $this->implements;
|
599 |
+
}
|
600 |
+
|
601 |
+
return array_filter(
|
602 |
+
$this->implements,
|
603 |
+
function ($cn) { return strpos($cn, 'Predis\\') === 0; }
|
604 |
+
);
|
605 |
+
}
|
606 |
+
|
607 |
+
public function getExtendedClasses($all = false)
|
608 |
+
{
|
609 |
+
if ($all) {
|
610 |
+
return $this->extemds;
|
611 |
+
}
|
612 |
+
|
613 |
+
return array_filter(
|
614 |
+
$this->extends,
|
615 |
+
function ($cn) { return strpos($cn, 'Predis\\') === 0; }
|
616 |
+
);
|
617 |
+
}
|
618 |
+
|
619 |
+
public function getDependencies($all = false)
|
620 |
+
{
|
621 |
+
return array_merge(
|
622 |
+
$this->getImplementedInterfaces($all),
|
623 |
+
$this->getExtendedClasses($all)
|
624 |
+
);
|
625 |
+
}
|
626 |
+
|
627 |
+
public function getNamespace()
|
628 |
+
{
|
629 |
+
return $this->namespace;
|
630 |
+
}
|
631 |
+
|
632 |
+
public function getFile()
|
633 |
+
{
|
634 |
+
return $this->file;
|
635 |
+
}
|
636 |
+
|
637 |
+
public function getName()
|
638 |
+
{
|
639 |
+
return $this->name;
|
640 |
+
}
|
641 |
+
|
642 |
+
public function getFQN()
|
643 |
+
{
|
644 |
+
return (string)$this->getNamespace() . '\\' . $this->name;
|
645 |
+
}
|
646 |
+
|
647 |
+
public function getPhpCode()
|
648 |
+
{
|
649 |
+
return $this->body;
|
650 |
+
}
|
651 |
+
|
652 |
+
public function __toString()
|
653 |
+
{
|
654 |
+
return "class " . $this->getName() . '{ ... }';
|
655 |
+
}
|
656 |
+
}
|
657 |
+
|
658 |
+
/* -------------------------------------------------------------------------- */
|
659 |
+
|
660 |
+
$options = CommandLine::getOptions();
|
661 |
+
$predisFile = PredisFile::from($options['source'], $options['exclude']);
|
662 |
+
$predisFile->saveTo($options['output']);
|
dependencies/vendor/predis/predis/examples/custom_cluster_distributor.php
ADDED
@@ -0,0 +1,117 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of the Predis package.
|
5 |
+
*
|
6 |
+
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
7 |
+
*
|
8 |
+
* For the full copyright and license information, please view the LICENSE
|
9 |
+
* file that was distributed with this source code.
|
10 |
+
*/
|
11 |
+
|
12 |
+
require __DIR__.'/shared.php';
|
13 |
+
|
14 |
+
// Developers can implement Predis\Distribution\DistributorInterface to create
|
15 |
+
// their own distributors used by the client to distribute keys among a cluster
|
16 |
+
// of servers.
|
17 |
+
|
18 |
+
use Predis\Cluster\Distributor\DistributorInterface;
|
19 |
+
use Predis\Cluster\Hash\HashGeneratorInterface;
|
20 |
+
use Predis\Cluster\PredisStrategy;
|
21 |
+
use Predis\Connection\Aggregate\PredisCluster;
|
22 |
+
|
23 |
+
class NaiveDistributor implements DistributorInterface, HashGeneratorInterface
|
24 |
+
{
|
25 |
+
private $nodes;
|
26 |
+
private $nodesCount;
|
27 |
+
|
28 |
+
public function __construct()
|
29 |
+
{
|
30 |
+
$this->nodes = array();
|
31 |
+
$this->nodesCount = 0;
|
32 |
+
}
|
33 |
+
|
34 |
+
public function add($node, $weight = null)
|
35 |
+
{
|
36 |
+
$this->nodes[] = $node;
|
37 |
+
++$this->nodesCount;
|
38 |
+
}
|
39 |
+
|
40 |
+
public function remove($node)
|
41 |
+
{
|
42 |
+
$this->nodes = array_filter($this->nodes, function ($n) use ($node) {
|
43 |
+
return $n !== $node;
|
44 |
+
});
|
45 |
+
|
46 |
+
$this->nodesCount = count($this->nodes);
|
47 |
+
}
|
48 |
+
|
49 |
+
public function getSlot($hash)
|
50 |
+
{
|
51 |
+
return $this->nodesCount > 1 ? abs($hash % $this->nodesCount) : 0;
|
52 |
+
}
|
53 |
+
|
54 |
+
public function getBySlot($slot)
|
55 |
+
{
|
56 |
+
return isset($this->nodes[$slot]) ? $this->nodes[$slot] : null;
|
57 |
+
}
|
58 |
+
|
59 |
+
public function getByHash($hash)
|
60 |
+
{
|
61 |
+
if (!$this->nodesCount) {
|
62 |
+
throw new RuntimeException('No connections.');
|
63 |
+
}
|
64 |
+
|
65 |
+
$slot = $this->getSlot($hash);
|
66 |
+
$node = $this->getBySlot($slot);
|
67 |
+
|
68 |
+
return $node;
|
69 |
+
}
|
70 |
+
|
71 |
+
public function get($value)
|
72 |
+
{
|
73 |
+
$hash = $this->hash($value);
|
74 |
+
$node = $this->getByHash($hash);
|
75 |
+
|
76 |
+
return $node;
|
77 |
+
}
|
78 |
+
|
79 |
+
public function hash($value)
|
80 |
+
{
|
81 |
+
return crc32($value);
|
82 |
+
}
|
83 |
+
|
84 |
+
public function getHashGenerator()
|
85 |
+
{
|
86 |
+
return $this;
|
87 |
+
}
|
88 |
+
}
|
89 |
+
|
90 |
+
$options = array(
|
91 |
+
'cluster' => function () {
|
92 |
+
$distributor = new NaiveDistributor();
|
93 |
+
$strategy = new PredisStrategy($distributor);
|
94 |
+
$cluster = new PredisCluster($strategy);
|
95 |
+
|
96 |
+
return $cluster;
|
97 |
+
},
|
98 |
+
);
|
99 |
+
|
100 |
+
$client = new Predis\Client($multiple_servers, $options);
|
101 |
+
|
102 |
+
for ($i = 0; $i < 100; ++$i) {
|
103 |
+
$client->set("key:$i", str_pad($i, 4, '0', 0));
|
104 |
+
$client->get("key:$i");
|
105 |
+
}
|
106 |
+
|
107 |
+
$server1 = $client->getClientFor('first')->info();
|
108 |
+
$server2 = $client->getClientFor('second')->info();
|
109 |
+
|
110 |
+
if (isset($server1['Keyspace'], $server2['Keyspace'])) {
|
111 |
+
$server1 = $server1['Keyspace'];
|
112 |
+
$server2 = $server2['Keyspace'];
|
113 |
+
}
|
114 |
+
|
115 |
+
printf("Server '%s' has %d keys while server '%s' has %d keys.\n",
|
116 |
+
'first', $server1['db15']['keys'], 'second', $server2['db15']['keys']
|
117 |
+
);
|
dependencies/vendor/predis/predis/examples/debuggable_connection.php
ADDED
@@ -0,0 +1,92 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of the Predis package.
|
5 |
+
*
|
6 |
+
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
7 |
+
*
|
8 |
+
* For the full copyright and license information, please view the LICENSE
|
9 |
+
* file that was distributed with this source code.
|
10 |
+
*/
|
11 |
+
|
12 |
+
require __DIR__.'/shared.php';
|
13 |
+
|
14 |
+
// This is an example of how you can easily extend an existing connection class
|
15 |
+
// and trace the execution of commands for debugging purposes. This can be quite
|
16 |
+
// useful as a starting poing to understand how your application interacts with
|
17 |
+
// Redis.
|
18 |
+
|
19 |
+
use Predis\Command\CommandInterface;
|
20 |
+
use Predis\Connection\StreamConnection;
|
21 |
+
|
22 |
+
class SimpleDebuggableConnection extends StreamConnection
|
23 |
+
{
|
24 |
+
private $tstart = 0;
|
25 |
+
private $debugBuffer = array();
|
26 |
+
|
27 |
+
public function connect()
|
28 |
+
{
|
29 |
+
$this->tstart = microtime(true);
|
30 |
+
|
31 |
+
parent::connect();
|
32 |
+
}
|
33 |
+
|
34 |
+
private function storeDebug(CommandInterface $command, $direction)
|
35 |
+
{
|
36 |
+
$firtsArg = $command->getArgument(0);
|
37 |
+
$timestamp = round(microtime(true) - $this->tstart, 4);
|
38 |
+
|
39 |
+
$debug = $command->getId();
|
40 |
+
$debug .= isset($firtsArg) ? " $firtsArg " : ' ';
|
41 |
+
$debug .= "$direction $this";
|
42 |
+
$debug .= " [{$timestamp}s]";
|
43 |
+
|
44 |
+
$this->debugBuffer[] = $debug;
|
45 |
+
}
|
46 |
+
|
47 |
+
public function writeRequest(CommandInterface $command)
|
48 |
+
{
|
49 |
+
parent::writeRequest($command);
|
50 |
+
|
51 |
+
$this->storeDebug($command, '->');
|
52 |
+
}
|
53 |
+
|
54 |
+
public function readResponse(CommandInterface $command)
|
55 |
+
{
|
56 |
+
$response = parent::readResponse($command);
|
57 |
+
$this->storeDebug($command, '<-');
|
58 |
+
|
59 |
+
return $response;
|
60 |
+
}
|
61 |
+
|
62 |
+
public function getDebugBuffer()
|
63 |
+
{
|
64 |
+
return $this->debugBuffer;
|
65 |
+
}
|
66 |
+
}
|
67 |
+
|
68 |
+
$options = array(
|
69 |
+
'connections' => array(
|
70 |
+
'tcp' => 'SimpleDebuggableConnection',
|
71 |
+
),
|
72 |
+
);
|
73 |
+
|
74 |
+
$client = new Predis\Client($single_server, $options);
|
75 |
+
$client->set('foo', 'bar');
|
76 |
+
$client->get('foo');
|
77 |
+
$client->info();
|
78 |
+
|
79 |
+
var_export($client->getConnection()->getDebugBuffer());
|
80 |
+
|
81 |
+
/* OUTPUT:
|
82 |
+
array (
|
83 |
+
0 => 'SELECT 15 -> 127.0.0.1:6379 [0.0008s]',
|
84 |
+
1 => 'SELECT 15 <- 127.0.0.1:6379 [0.001s]',
|
85 |
+
2 => 'SET foo -> 127.0.0.1:6379 [0.001s]',
|
86 |
+
3 => 'SET foo <- 127.0.0.1:6379 [0.0011s]',
|
87 |
+
4 => 'GET foo -> 127.0.0.1:6379 [0.0013s]',
|
88 |
+
5 => 'GET foo <- 127.0.0.1:6379 [0.0015s]',
|
89 |
+
6 => 'INFO -> 127.0.0.1:6379 [0.0019s]',
|
90 |
+
7 => 'INFO <- 127.0.0.1:6379 [0.0022s]',
|
91 |
+
)
|
92 |
+
*/
|
dependencies/vendor/predis/predis/examples/dispatcher_loop.php
ADDED
@@ -0,0 +1,79 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of the Predis package.
|
5 |
+
*
|
6 |
+
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
7 |
+
*
|
8 |
+
* For the full copyright and license information, please view the LICENSE
|
9 |
+
* file that was distributed with this source code.
|
10 |
+
*/
|
11 |
+
|
12 |
+
require __DIR__.'/shared.php';
|
13 |
+
|
14 |
+
// This is a basic example on how to use the Predis\DispatcherLoop class.
|
15 |
+
//
|
16 |
+
// To see this example in action you can just use redis-cli and publish some
|
17 |
+
// messages to the 'events' and 'control' channel, e.g.:
|
18 |
+
|
19 |
+
// ./redis-cli
|
20 |
+
// PUBLISH events first
|
21 |
+
// PUBLISH events second
|
22 |
+
// PUBLISH events third
|
23 |
+
// PUBLISH control terminate_dispatcher
|
24 |
+
|
25 |
+
// Create a client and disable r/w timeout on the socket
|
26 |
+
$client = new Predis\Client($single_server + array('read_write_timeout' => 0));
|
27 |
+
|
28 |
+
// Return an initialized PubSub consumer instance from the client.
|
29 |
+
$pubsub = $client->pubSubLoop();
|
30 |
+
|
31 |
+
// Create a dispatcher loop instance and attach a bunch of callbacks.
|
32 |
+
$dispatcher = new Predis\PubSub\DispatcherLoop($pubsub);
|
33 |
+
|
34 |
+
// Demonstrate how to use a callable class as a callback for the dispatcher loop.
|
35 |
+
class EventsListener implements Countable
|
36 |
+
{
|
37 |
+
private $events;
|
38 |
+
|
39 |
+
public function __construct()
|
40 |
+
{
|
41 |
+
$this->events = array();
|
42 |
+
}
|
43 |
+
|
44 |
+
public function count()
|
45 |
+
{
|
46 |
+
return count($this->events);
|
47 |
+
}
|
48 |
+
|
49 |
+
public function getEvents()
|
50 |
+
{
|
51 |
+
return $this->events;
|
52 |
+
}
|
53 |
+
|
54 |
+
public function __invoke($payload)
|
55 |
+
{
|
56 |
+
$this->events[] = $payload;
|
57 |
+
}
|
58 |
+
}
|
59 |
+
|
60 |
+
// Attach our callable class to the dispatcher.
|
61 |
+
$dispatcher->attachCallback('events', ($events = new EventsListener()));
|
62 |
+
|
63 |
+
// Attach a function to control the dispatcher loop termination with a message.
|
64 |
+
$dispatcher->attachCallback('control', function ($payload) use ($dispatcher) {
|
65 |
+
if ($payload === 'terminate_dispatcher') {
|
66 |
+
$dispatcher->stop();
|
67 |
+
}
|
68 |
+
});
|
69 |
+
|
70 |
+
// Run the dispatcher loop until the callback attached to the 'control' channel
|
71 |
+
// receives 'terminate_dispatcher' as a message.
|
72 |
+
$dispatcher->run();
|
73 |
+
|
74 |
+
// Display our achievements!
|
75 |
+
echo "We received {$events->count()} messages!", PHP_EOL;
|
76 |
+
|
77 |
+
// Say goodbye :-)
|
78 |
+
$version = redis_version($client->info());
|
79 |
+
echo "Goodbye from Redis $version!", PHP_EOL;
|
dependencies/vendor/predis/predis/examples/executing_redis_commands.php
ADDED
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of the Predis package.
|
5 |
+
*
|
6 |
+
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
7 |
+
*
|
8 |
+
* For the full copyright and license information, please view the LICENSE
|
9 |
+
* file that was distributed with this source code.
|
10 |
+
*/
|
11 |
+
|
12 |
+
require __DIR__.'/shared.php';
|
13 |
+
|
14 |
+
$client = new Predis\Client($single_server);
|
15 |
+
|
16 |
+
// Plain old SET and GET example...
|
17 |
+
$client->set('library', 'predis');
|
18 |
+
$response = $client->get('library');
|
19 |
+
|
20 |
+
var_export($response); echo PHP_EOL;
|
21 |
+
/* OUTPUT: 'predis' */
|
22 |
+
|
23 |
+
// Redis has the MSET and MGET commands to set or get multiple keys in one go,
|
24 |
+
// cases like this Predis accepts arguments for variadic commands both as a list
|
25 |
+
// of arguments or an array containing all of the keys and/or values.
|
26 |
+
$mkv = array(
|
27 |
+
'uid:0001' => '1st user',
|
28 |
+
'uid:0002' => '2nd user',
|
29 |
+
'uid:0003' => '3rd user',
|
30 |
+
);
|
31 |
+
|
32 |
+
$client->mset($mkv);
|
33 |
+
$response = $client->mget(array_keys($mkv));
|
34 |
+
|
35 |
+
var_export($response); echo PHP_EOL;
|
36 |
+
/* OUTPUT:
|
37 |
+
array (
|
38 |
+
0 => '1st user',
|
39 |
+
1 => '2nd user',
|
40 |
+
2 => '3rd user',
|
41 |
+
) */
|
42 |
+
|
43 |
+
// Predis can also send "raw" commands to Redis. The difference between sending
|
44 |
+
// commands to Redis the usual way and the "raw" way is that in the latter case
|
45 |
+
// their arguments are not filtered nor responses coming from Redis are parsed.
|
46 |
+
|
47 |
+
$response = $client->executeRaw(array(
|
48 |
+
'MGET', 'uid:0001', 'uid:0002', 'uid:0003',
|
49 |
+
));
|
50 |
+
|
51 |
+
var_export($response); echo PHP_EOL;
|
52 |
+
/* OUTPUT:
|
53 |
+
array (
|
54 |
+
0 => '1st user',
|
55 |
+
1 => '2nd user',
|
56 |
+
2 => '3rd user',
|
57 |
+
) */
|
dependencies/vendor/predis/predis/examples/key_prefixing.php
ADDED
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of the Predis package.
|
5 |
+
*
|
6 |
+
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
7 |
+
*
|
8 |
+
* For the full copyright and license information, please view the LICENSE
|
9 |
+
* file that was distributed with this source code.
|
10 |
+
*/
|
11 |
+
|
12 |
+
require __DIR__.'/shared.php';
|
13 |
+
|
14 |
+
// Predis can prefix keys found in commands arguments before sending commands to
|
15 |
+
// Redis, even for complex commands such as SORT, ZUNIONSTORE and ZINTERSTORE.
|
16 |
+
// Prefixing keys can be useful to create user-level namespaces for you keyspace
|
17 |
+
// thus reducing the need for separate logical databases in certain scenarios.
|
18 |
+
|
19 |
+
$client = new Predis\Client($single_server, array('prefix' => 'nrk:'));
|
20 |
+
|
21 |
+
$client->mset(array('foo' => 'bar', 'lol' => 'wut'));
|
22 |
+
var_export($client->mget('foo', 'lol'));
|
23 |
+
/*
|
24 |
+
array (
|
25 |
+
0 => 'bar',
|
26 |
+
1 => 'wut',
|
27 |
+
)
|
28 |
+
*/
|
29 |
+
|
30 |
+
var_export($client->keys('*'));
|
31 |
+
/*
|
32 |
+
array (
|
33 |
+
0 => 'nrk:foo',
|
34 |
+
1 => 'nrk:lol',
|
35 |
+
)
|
36 |
+
*/
|
dependencies/vendor/predis/predis/examples/lua_scripting_abstraction.php
ADDED
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of the Predis package.
|
5 |
+
*
|
6 |
+
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
7 |
+
*
|
8 |
+
* For the full copyright and license information, please view the LICENSE
|
9 |
+
* file that was distributed with this source code.
|
10 |
+
*/
|
11 |
+
|
12 |
+
require __DIR__.'/shared.php';
|
13 |
+
|
14 |
+
// This example will not work with versions of Redis < 2.6.
|
15 |
+
//
|
16 |
+
// Additionally to the EVAL command defined in the current development profile,
|
17 |
+
// the Predis\Command\ScriptCommand class can be used to build an higher level
|
18 |
+
// abstraction for "scriptable" commands so that they will appear just like any
|
19 |
+
// other command on the client-side. This is a quick example used to implement
|
20 |
+
// INCREX.
|
21 |
+
|
22 |
+
use Predis\Command\ScriptCommand;
|
23 |
+
|
24 |
+
class IncrementExistingKeysBy extends ScriptCommand
|
25 |
+
{
|
26 |
+
public function getKeysCount()
|
27 |
+
{
|
28 |
+
// Tell Predis to use all the arguments but the last one as arguments
|
29 |
+
// for KEYS. The last one will be used to populate ARGV.
|
30 |
+
return -1;
|
31 |
+
}
|
32 |
+
|
33 |
+
public function getScript()
|
34 |
+
{
|
35 |
+
return <<<LUA
|
36 |
+
local cmd, insert = redis.call, table.insert
|
37 |
+
local increment, results = ARGV[1], { }
|
38 |
+
|
39 |
+
for idx, key in ipairs(KEYS) do
|
40 |
+
if cmd('exists', key) == 1 then
|
41 |
+
insert(results, idx, cmd('incrby', key, increment))
|
42 |
+
else
|
43 |
+
insert(results, idx, false)
|
44 |
+
end
|
45 |
+
end
|
46 |
+
|
47 |
+
return results
|
48 |
+
LUA;
|
49 |
+
}
|
50 |
+
}
|
51 |
+
|
52 |
+
$client = new Predis\Client($single_server, array(
|
53 |
+
'profile' => function ($options) {
|
54 |
+
$profile = $options->getDefault('profile');
|
55 |
+
$profile->defineCommand('increxby', 'IncrementExistingKeysBy');
|
56 |
+
|
57 |
+
return $profile;
|
58 |
+
},
|
59 |
+
));
|
60 |
+
|
61 |
+
$client->mset('foo', 10, 'foobar', 100);
|
62 |
+
|
63 |
+
var_export($client->increxby('foo', 'foofoo', 'foobar', 50));
|
64 |
+
|
65 |
+
/*
|
66 |
+
array (
|
67 |
+
0 => 60,
|
68 |
+
1 => NULL,
|
69 |
+
2 => 150,
|
70 |
+
)
|
71 |
+
*/
|
dependencies/vendor/predis/predis/examples/monitor_consumer.php
ADDED
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of the Predis package.
|
5 |
+
*
|
6 |
+
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
7 |
+
*
|
8 |
+
* For the full copyright and license information, please view the LICENSE
|
9 |
+
* file that was distributed with this source code.
|
10 |
+
*/
|
11 |
+
|
12 |
+
require __DIR__.'/shared.php';
|
13 |
+
|
14 |
+
// This is a basic example on how to use the Predis\Monitor\Consumer class. You
|
15 |
+
// can use redis-cli to send commands to the same Redis instance your client is
|
16 |
+
// connected to, and then type "ECHO QUIT_MONITOR" in redis-cli when you want to
|
17 |
+
// exit the monitor loop and terminate this script in a graceful way.
|
18 |
+
|
19 |
+
// Create a client and disable r/w timeout on the socket.
|
20 |
+
$client = new Predis\Client($single_server + array('read_write_timeout' => 0));
|
21 |
+
|
22 |
+
// Use only one instance of DateTime, we will update the timestamp later.
|
23 |
+
$timestamp = new DateTime();
|
24 |
+
|
25 |
+
foreach (($monitor = $client->monitor()) as $event) {
|
26 |
+
$timestamp->setTimestamp((int) $event->timestamp);
|
27 |
+
|
28 |
+
// If we notice a ECHO command with the message QUIT_MONITOR, we stop the
|
29 |
+
// monitor consumer and then break the loop.
|
30 |
+
if ($event->command === 'ECHO' && $event->arguments === '"QUIT_MONITOR"') {
|
31 |
+
echo 'Exiting the monitor loop...', PHP_EOL;
|
32 |
+
$monitor->stop();
|
33 |
+
break;
|
34 |
+
}
|
35 |
+
|
36 |
+
echo "* Received {$event->command} on DB {$event->database} at {$timestamp->format(DateTime::W3C)}", PHP_EOL;
|
37 |
+
if (isset($event->arguments)) {
|
38 |
+
echo " Arguments: {$event->arguments}", PHP_EOL;
|
39 |
+
}
|
40 |
+
}
|
41 |
+
|
42 |
+
// Say goodbye :-)
|
43 |
+
$version = redis_version($client->info());
|
44 |
+
echo "Goodbye from Redis $version!", PHP_EOL;
|
dependencies/vendor/predis/predis/examples/pipelining_commands.php
ADDED
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of the Predis package.
|
5 |
+
*
|
6 |
+
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
7 |
+
*
|
8 |
+
* For the full copyright and license information, please view the LICENSE
|
9 |
+
* file that was distributed with this source code.
|
10 |
+
*/
|
11 |
+
|
12 |
+
require __DIR__.'/shared.php';
|
13 |
+
|
14 |
+
// When you have a whole set of consecutive commands to send to a redis server,
|
15 |
+
// you can use a pipeline to dramatically improve performances. Pipelines can
|
16 |
+
// greatly reduce the effects of network round-trips.
|
17 |
+
|
18 |
+
$client = new Predis\Client($single_server);
|
19 |
+
|
20 |
+
$responses = $client->pipeline(function ($pipe) {
|
21 |
+
$pipe->flushdb();
|
22 |
+
$pipe->incrby('counter', 10);
|
23 |
+
$pipe->incrby('counter', 30);
|
24 |
+
$pipe->exists('counter');
|
25 |
+
$pipe->get('counter');
|
26 |
+
$pipe->mget('does_not_exist', 'counter');
|
27 |
+
});
|
28 |
+
|
29 |
+
var_export($responses);
|
30 |
+
|
31 |
+
/* OUTPUT:
|
32 |
+
array (
|
33 |
+
0 => Predis\Response\Status::__set_state(array(
|
34 |
+
'payload' => 'OK',
|
35 |
+
)),
|
36 |
+
1 => 10,
|
37 |
+
2 => 40,
|
38 |
+
3 => true,
|
39 |
+
4 => '40',
|
40 |
+
5 => array (
|
41 |
+
0 => NULL,
|
42 |
+
1 => '40',
|
43 |
+
),
|
44 |
+
)
|
45 |
+
*/
|
dependencies/vendor/predis/predis/examples/pubsub_consumer.php
ADDED
@@ -0,0 +1,59 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of the Predis package.
|
5 |
+
*
|
6 |
+
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
7 |
+
*
|
8 |
+
* For the full copyright and license information, please view the LICENSE
|
9 |
+
* file that was distributed with this source code.
|
10 |
+
*/
|
11 |
+
|
12 |
+
require __DIR__.'/shared.php';
|
13 |
+
|
14 |
+
// Starting from Redis 2.0 clients can subscribe and listen for events published
|
15 |
+
// on certain channels using a Publish/Subscribe (PUB/SUB) approach.
|
16 |
+
|
17 |
+
// Create a client and disable r/w timeout on the socket
|
18 |
+
$client = new Predis\Client($single_server + array('read_write_timeout' => 0));
|
19 |
+
|
20 |
+
// Initialize a new pubsub consumer.
|
21 |
+
$pubsub = $client->pubSubLoop();
|
22 |
+
|
23 |
+
// Subscribe to your channels
|
24 |
+
$pubsub->subscribe('control_channel', 'notifications');
|
25 |
+
|
26 |
+
// Start processing the pubsup messages. Open a terminal and use redis-cli
|
27 |
+
// to push messages to the channels. Examples:
|
28 |
+
// ./redis-cli PUBLISH notifications "this is a test"
|
29 |
+
// ./redis-cli PUBLISH control_channel quit_loop
|
30 |
+
foreach ($pubsub as $message) {
|
31 |
+
switch ($message->kind) {
|
32 |
+
case 'subscribe':
|
33 |
+
echo "Subscribed to {$message->channel}", PHP_EOL;
|
34 |
+
break;
|
35 |
+
|
36 |
+
case 'message':
|
37 |
+
if ($message->channel == 'control_channel') {
|
38 |
+
if ($message->payload == 'quit_loop') {
|
39 |
+
echo 'Aborting pubsub loop...', PHP_EOL;
|
40 |
+
$pubsub->unsubscribe();
|
41 |
+
} else {
|
42 |
+
echo "Received an unrecognized command: {$message->payload}.", PHP_EOL;
|
43 |
+
}
|
44 |
+
} else {
|
45 |
+
echo "Received the following message from {$message->channel}:",
|
46 |
+
PHP_EOL, " {$message->payload}", PHP_EOL, PHP_EOL;
|
47 |
+
}
|
48 |
+
break;
|
49 |
+
}
|
50 |
+
}
|
51 |
+
|
52 |
+
// Always unset the pubsub consumer instance when you are done! The
|
53 |
+
// class destructor will take care of cleanups and prevent protocol
|
54 |
+
// desynchronizations between the client and the server.
|
55 |
+
unset($pubsub);
|
56 |
+
|
57 |
+
// Say goodbye :-)
|
58 |
+
$version = redis_version($client->info());
|
59 |
+
echo "Goodbye from Redis $version!", PHP_EOL;
|
dependencies/vendor/predis/predis/examples/redis_collections_iterators.php
ADDED
@@ -0,0 +1,99 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of the Predis package.
|
5 |
+
*
|
6 |
+
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
7 |
+
*
|
8 |
+
* For the full copyright and license information, please view the LICENSE
|
9 |
+
* file that was distributed with this source code.
|
10 |
+
*/
|
11 |
+
|
12 |
+
require __DIR__.'/shared.php';
|
13 |
+
|
14 |
+
use Predis\Collection\Iterator;
|
15 |
+
|
16 |
+
// Starting from Redis 2.8, clients can iterate incrementally over collections
|
17 |
+
// without blocking the server like it happens when a command such as KEYS is
|
18 |
+
// executed on a Redis instance storing millions of keys. These commands are:
|
19 |
+
//
|
20 |
+
// - SCAN (iterates over the keyspace)
|
21 |
+
// - SSCAN (iterates over members of a set)
|
22 |
+
// - ZSCAN (iterates over members and ranks of a sorted set)
|
23 |
+
// - HSCAN (iterates over fields and values of an hash).
|
24 |
+
|
25 |
+
// Predis provides a specialized abstraction for each command based on standard
|
26 |
+
// SPL iterators making it possible to easily consume SCAN-based iterations in
|
27 |
+
// your PHP code.
|
28 |
+
//
|
29 |
+
// See http://redis.io/commands/scan for more details.
|
30 |
+
//
|
31 |
+
|
32 |
+
// Create a client using `2.8` as a server profile (needs Redis 2.8!)
|
33 |
+
$client = new Predis\Client($single_server, array('profile' => '2.8'));
|
34 |
+
|
35 |
+
// Prepare some keys for our example
|
36 |
+
$client->del('predis:set', 'predis:zset', 'predis:hash');
|
37 |
+
for ($i = 0; $i < 5; ++$i) {
|
38 |
+
$client->sadd('predis:set', "member:$i");
|
39 |
+
$client->zadd('predis:zset', -$i, "member:$i");
|
40 |
+
$client->hset('predis:hash', "field:$i", "value:$i");
|
41 |
+
}
|
42 |
+
|
43 |
+
// === Keyspace iterator based on SCAN ===
|
44 |
+
echo 'Scan the keyspace matching only our prefixed keys:', PHP_EOL;
|
45 |
+
foreach (new Iterator\Keyspace($client, 'predis:*') as $key) {
|
46 |
+
echo " - $key", PHP_EOL;
|
47 |
+
}
|
48 |
+
|
49 |
+
/* OUTPUT
|
50 |
+
Scan the keyspace matching only our prefixed keys:
|
51 |
+
- predis:zset
|
52 |
+
- predis:set
|
53 |
+
- predis:hash
|
54 |
+
*/
|
55 |
+
|
56 |
+
// === Set iterator based on SSCAN ===
|
57 |
+
echo 'Scan members of `predis:set`:', PHP_EOL;
|
58 |
+
foreach (new Iterator\SetKey($client, 'predis:set') as $member) {
|
59 |
+
echo " - $member", PHP_EOL;
|
60 |
+
}
|
61 |
+
|
62 |
+
/* OUTPUT
|
63 |
+
Scan members of `predis:set`:
|
64 |
+
- member:1
|
65 |
+
- member:4
|
66 |
+
- member:0
|
67 |
+
- member:3
|
68 |
+
- member:2
|
69 |
+
*/
|
70 |
+
|
71 |
+
// === Sorted set iterator based on ZSCAN ===
|
72 |
+
echo 'Scan members and ranks of `predis:zset`:', PHP_EOL;
|
73 |
+
foreach (new Iterator\SortedSetKey($client, 'predis:zset') as $member => $rank) {
|
74 |
+
echo " - $member [rank: $rank]", PHP_EOL;
|
75 |
+
}
|
76 |
+
|
77 |
+
/* OUTPUT
|
78 |
+
Scan members and ranks of `predis:zset`:
|
79 |
+
- member:4 [rank: -4]
|
80 |
+
- member:3 [rank: -3]
|
81 |
+
- member:2 [rank: -2]
|
82 |
+
- member:1 [rank: -1]
|
83 |
+
- member:0 [rank: 0]
|
84 |
+
*/
|
85 |
+
|
86 |
+
// === Hash iterator based on HSCAN ===
|
87 |
+
echo 'Scan fields and values of `predis:hash`:', PHP_EOL;
|
88 |
+
foreach (new Iterator\HashKey($client, 'predis:hash') as $field => $value) {
|
89 |
+
echo " - $field => $value", PHP_EOL;
|
90 |
+
}
|
91 |
+
|
92 |
+
/* OUTPUT
|
93 |
+
Scan fields and values of `predis:hash`:
|
94 |
+
- field:0 => value:0
|
95 |
+
- field:1 => value:1
|
96 |
+
- field:2 => value:2
|
97 |
+
- field:3 => value:3
|
98 |
+
- field:4 => value:4
|
99 |
+
*/
|
dependencies/vendor/predis/predis/examples/replication_complex.php
ADDED
@@ -0,0 +1,85 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of the Predis package.
|
5 |
+
*
|
6 |
+
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
7 |
+
*
|
8 |
+
* For the full copyright and license information, please view the LICENSE
|
9 |
+
* file that was distributed with this source code.
|
10 |
+
*/
|
11 |
+
|
12 |
+
require __DIR__.'/shared.php';
|
13 |
+
|
14 |
+
// Predis allows to set Lua scripts as read-only operations for replication.
|
15 |
+
// This works for both EVAL and EVALSHA and also for the client-side abstraction
|
16 |
+
// built upon them (Predis\Command\ScriptCommand). This example shows a slightly
|
17 |
+
// more complex configuration that injects a new script command in the server
|
18 |
+
// profile used by the new client instance and marks it marks it as a read-only
|
19 |
+
// operation for replication so that it will be executed on slaves.
|
20 |
+
|
21 |
+
use Predis\Command\ScriptCommand;
|
22 |
+
use Predis\Connection\Aggregate\MasterSlaveReplication;
|
23 |
+
use Predis\Replication\ReplicationStrategy;
|
24 |
+
|
25 |
+
// ------------------------------------------------------------------------- //
|
26 |
+
|
27 |
+
// Define a new script command that returns all the fields of a variable number
|
28 |
+
// of hashes with a single roundtrip.
|
29 |
+
|
30 |
+
class HashMultipleGetAll extends ScriptCommand
|
31 |
+
{
|
32 |
+
const BODY = <<<LUA
|
33 |
+
local hashes = {}
|
34 |
+
for _, key in pairs(KEYS) do
|
35 |
+
table.insert(hashes, key)
|
36 |
+
table.insert(hashes, redis.call('hgetall', key))
|
37 |
+
end
|
38 |
+
return hashes
|
39 |
+
LUA;
|
40 |
+
|
41 |
+
public function getScript()
|
42 |
+
{
|
43 |
+
return self::BODY;
|
44 |
+
}
|
45 |
+
}
|
46 |
+
|
47 |
+
// ------------------------------------------------------------------------- //
|
48 |
+
|
49 |
+
$parameters = array(
|
50 |
+
'tcp://127.0.0.1:6379/?alias=master',
|
51 |
+
'tcp://127.0.0.1:6380/?alias=slave',
|
52 |
+
);
|
53 |
+
|
54 |
+
$options = array(
|
55 |
+
'profile' => function ($options, $option) {
|
56 |
+
$profile = $options->getDefault($option);
|
57 |
+
$profile->defineCommand('hmgetall', 'HashMultipleGetAll');
|
58 |
+
|
59 |
+
return $profile;
|
60 |
+
},
|
61 |
+
'replication' => function () {
|
62 |
+
$strategy = new ReplicationStrategy();
|
63 |
+
$strategy->setScriptReadOnly(HashMultipleGetAll::BODY);
|
64 |
+
|
65 |
+
$replication = new MasterSlaveReplication($strategy);
|
66 |
+
|
67 |
+
return $replication;
|
68 |
+
},
|
69 |
+
);
|
70 |
+
|
71 |
+
// ------------------------------------------------------------------------- //
|
72 |
+
|
73 |
+
$client = new Predis\Client($parameters, $options);
|
74 |
+
|
75 |
+
// Execute the following commands on the master server using redis-cli:
|
76 |
+
// $ ./redis-cli HMSET metavars foo bar hoge piyo
|
77 |
+
// $ ./redis-cli HMSET servers master host1 slave host2
|
78 |
+
|
79 |
+
$hashes = $client->hmgetall('metavars', 'servers');
|
80 |
+
|
81 |
+
$replication = $client->getConnection();
|
82 |
+
$stillOnSlave = $replication->getCurrent() === $replication->getConnectionById('slave');
|
83 |
+
|
84 |
+
echo 'Is still on slave? ', $stillOnSlave ? 'YES!' : 'NO!', PHP_EOL;
|
85 |
+
var_export($hashes);
|
dependencies/vendor/predis/predis/examples/replication_sentinel.php
ADDED
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of the Predis package.
|
5 |
+
*
|
6 |
+
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
7 |
+
*
|
8 |
+
* For the full copyright and license information, please view the LICENSE
|
9 |
+
* file that was distributed with this source code.
|
10 |
+
*/
|
11 |
+
|
12 |
+
require __DIR__.'/shared.php';
|
13 |
+
|
14 |
+
// Predis supports redis-sentinel to provide high availability in master / slave
|
15 |
+
// scenarios. The only but relevant difference with a basic replication scenario
|
16 |
+
// is that sentinel servers can manage the master server and its slaves based on
|
17 |
+
// their state, which means that they are able to provide an authoritative and
|
18 |
+
// updated configuration to clients thus avoiding static configurations for the
|
19 |
+
// replication servers and their roles.
|
20 |
+
|
21 |
+
// Instead of connection parameters pointing to redis nodes, we provide a list
|
22 |
+
// of instances of redis-sentinel. Users should always provide a timeout value
|
23 |
+
// low enough to not hinder operations just in case a sentinel is unreachable
|
24 |
+
// but Predis uses a default value of 100 milliseconds for sentinel parameters
|
25 |
+
// without an explicit timeout value.
|
26 |
+
//
|
27 |
+
// NOTE: in real-world scenarios sentinels should be running on different hosts!
|
28 |
+
$sentinels = array(
|
29 |
+
'tcp://127.0.0.1:5380?timeout=0.100',
|
30 |
+
'tcp://127.0.0.1:5381?timeout=0.100',
|
31 |
+
'tcp://127.0.0.1:5382?timeout=0.100',
|
32 |
+
);
|
33 |
+
|
34 |
+
$client = new Predis\Client($sentinels, array(
|
35 |
+
'replication' => 'sentinel',
|
36 |
+
'service' => 'mymaster',
|
37 |
+
));
|
38 |
+
|
39 |
+
// Read operation.
|
40 |
+
$exists = $client->exists('foo') ? 'yes' : 'no';
|
41 |
+
$current = $client->getConnection()->getCurrent()->getParameters();
|
42 |
+
echo "Does 'foo' exist on {$current->alias}? $exists.", PHP_EOL;
|
43 |
+
|
44 |
+
// Write operation.
|
45 |
+
$client->set('foo', 'bar');
|
46 |
+
$current = $client->getConnection()->getCurrent()->getParameters();
|
47 |
+
echo "Now 'foo' has been set to 'bar' on {$current->alias}!", PHP_EOL;
|
48 |
+
|
49 |
+
// Read operation.
|
50 |
+
$bar = $client->get('foo');
|
51 |
+
$current = $client->getConnection()->getCurrent()->getParameters();
|
52 |
+
echo "We fetched 'foo' from {$current->alias} and its value is '$bar'.", PHP_EOL;
|
53 |
+
|
54 |
+
/* OUTPUT:
|
55 |
+
Does 'foo' exist on slave-127.0.0.1:6381? yes.
|
56 |
+
Now 'foo' has been set to 'bar' on master!
|
57 |
+
We fetched 'foo' from master and its value is 'bar'.
|
58 |
+
*/
|
dependencies/vendor/predis/predis/examples/replication_simple.php
ADDED
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of the Predis package.
|
5 |
+
*
|
6 |
+
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
7 |
+
*
|
8 |
+
* For the full copyright and license information, please view the LICENSE
|
9 |
+
* file that was distributed with this source code.
|
10 |
+
*/
|
11 |
+
|
12 |
+
require __DIR__.'/shared.php';
|
13 |
+
|
14 |
+
// Predis supports master / slave replication scenarios where write operations
|
15 |
+
// are performed on the master server and read operations are executed against
|
16 |
+
// one of the slaves. The behavior of commands or EVAL scripts can be customized
|
17 |
+
// at will. As soon as a write operation is performed the client switches to the
|
18 |
+
// master server for all the subsequent requests (either reads and writes).
|
19 |
+
//
|
20 |
+
// This example must be executed using the second Redis server configured as the
|
21 |
+
// slave of the first one (see the "SLAVEOF" command).
|
22 |
+
//
|
23 |
+
|
24 |
+
$parameters = array(
|
25 |
+
'tcp://127.0.0.1:6379?database=15&alias=master',
|
26 |
+
'tcp://127.0.0.1:6380?database=15&alias=slave',
|
27 |
+
);
|
28 |
+
|
29 |
+
$options = array('replication' => true);
|
30 |
+
|
31 |
+
$client = new Predis\Client($parameters, $options);
|
32 |
+
|
33 |
+
// Read operation.
|
34 |
+
$exists = $client->exists('foo') ? 'yes' : 'no';
|
35 |
+
$current = $client->getConnection()->getCurrent()->getParameters();
|
36 |
+
echo "Does 'foo' exist on {$current->alias}? $exists.", PHP_EOL;
|
37 |
+
|
38 |
+
// Write operation.
|
39 |
+
$client->set('foo', 'bar');
|
40 |
+
$current = $client->getConnection()->getCurrent()->getParameters();
|
41 |
+
echo "Now 'foo' has been set to 'bar' on {$current->alias}!", PHP_EOL;
|
42 |
+
|
43 |
+
// Read operation.
|
44 |
+
$bar = $client->get('foo');
|
45 |
+
$current = $client->getConnection()->getCurrent()->getParameters();
|
46 |
+
echo "We fetched 'foo' from {$current->alias} and its value is '$bar'.", PHP_EOL;
|
47 |
+
|
48 |
+
/* OUTPUT:
|
49 |
+
Does 'foo' exist on slave? yes.
|
50 |
+
Now 'foo' has been set to 'bar' on master!
|
51 |
+
We fetched 'foo' from master and its value is 'bar'.
|
52 |
+
*/
|
dependencies/vendor/predis/predis/examples/session_handler.php
ADDED
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of the Predis package.
|
5 |
+
*
|
6 |
+
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
7 |
+
*
|
8 |
+
* For the full copyright and license information, please view the LICENSE
|
9 |
+
* file that was distributed with this source code.
|
10 |
+
*/
|
11 |
+
|
12 |
+
require __DIR__.'/shared.php';
|
13 |
+
|
14 |
+
// This example demonstrates how to use Predis to save PHP sessions on Redis.
|
15 |
+
//
|
16 |
+
// The value of `session.gc_maxlifetime` in `php.ini` will be used by default as
|
17 |
+
// the TTL for keys holding session data but this value can be overridden when
|
18 |
+
// creating the session handler instance using the `gc_maxlifetime` option.
|
19 |
+
//
|
20 |
+
// NOTE: this class requires PHP >= 5.4 but can be used on PHP 5.3 if a polyfill
|
21 |
+
// for SessionHandlerInterface is provided either by you or an external package
|
22 |
+
// like `symfony/http-foundation`.
|
23 |
+
//
|
24 |
+
// See http://www.php.net/class.sessionhandlerinterface.php for more details.
|
25 |
+
//
|
26 |
+
|
27 |
+
if (!interface_exists('SessionHandlerInterface')) {
|
28 |
+
die('ATTENTION: the session handler implemented by Predis requires PHP >= 5.4.0 '.
|
29 |
+
"or a polyfill for SessionHandlerInterface provided by an external package.\n");
|
30 |
+
}
|
31 |
+
|
32 |
+
// Instantiate a new client just like you would normally do. Using a prefix for
|
33 |
+
// keys will effectively prefix all session keys with the specified string.
|
34 |
+
$client = new Predis\Client($single_server, array('prefix' => 'sessions:'));
|
35 |
+
|
36 |
+
// Set `gc_maxlifetime` to specify a time-to-live of 5 seconds for session keys.
|
37 |
+
$handler = new Predis\Session\Handler($client, array('gc_maxlifetime' => 5));
|
38 |
+
|
39 |
+
// Register the session handler.
|
40 |
+
$handler->register();
|
41 |
+
|
42 |
+
// We just set a fixed session ID only for the sake of our example.
|
43 |
+
session_id('example_session_id');
|
44 |
+
|
45 |
+
session_start();
|
46 |
+
|
47 |
+
if (isset($_SESSION['foo'])) {
|
48 |
+
echo "Session has `foo` set to {$_SESSION['foo']}", PHP_EOL;
|
49 |
+
} else {
|
50 |
+
$_SESSION['foo'] = $value = mt_rand();
|
51 |
+
echo "Empty session, `foo` has been set with $value", PHP_EOL;
|
52 |
+
}
|
dependencies/vendor/predis/predis/examples/shared.php
ADDED
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of the Predis package.
|
5 |
+
*
|
6 |
+
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
7 |
+
*
|
8 |
+
* For the full copyright and license information, please view the LICENSE
|
9 |
+
* file that was distributed with this source code.
|
10 |
+
*/
|
11 |
+
|
12 |
+
require __DIR__.'/../autoload.php';
|
13 |
+
|
14 |
+
function redis_version($info)
|
15 |
+
{
|
16 |
+
if (isset($info['Server']['redis_version'])) {
|
17 |
+
return $info['Server']['redis_version'];
|
18 |
+
} elseif (isset($info['redis_version'])) {
|
19 |
+
return $info['redis_version'];
|
20 |
+
} else {
|
21 |
+
return 'unknown version';
|
22 |
+
}
|
23 |
+
}
|
24 |
+
|
25 |
+
$single_server = array(
|
26 |
+
'host' => '127.0.0.1',
|
27 |
+
'port' => 6379,
|
28 |
+
'database' => 15,
|
29 |
+
);
|
30 |
+
|
31 |
+
$multiple_servers = array(
|
32 |
+
array(
|
33 |
+
'host' => '127.0.0.1',
|
34 |
+
'port' => 6379,
|
35 |
+
'database' => 15,
|
36 |
+
'alias' => 'first',
|
37 |
+
),
|
38 |
+
array(
|
39 |
+
'host' => '127.0.0.1',
|
40 |
+
'port' => 6380,
|
41 |
+
'database' => 15,
|
42 |
+
'alias' => 'second',
|
43 |
+
),
|
44 |
+
);
|
dependencies/vendor/predis/predis/examples/transaction_using_cas.php
ADDED
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of the Predis package.
|
5 |
+
*
|
6 |
+
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
7 |
+
*
|
8 |
+
* For the full copyright and license information, please view the LICENSE
|
9 |
+
* file that was distributed with this source code.
|
10 |
+
*/
|
11 |
+
|
12 |
+
require __DIR__.'/shared.php';
|
13 |
+
|
14 |
+
// This is an implementation of an atomic client-side ZPOP using the support for
|
15 |
+
// check-and-set (CAS) operations with MULTI/EXEC transactions, as described in
|
16 |
+
// "WATCH explained" from http://redis.io/topics/transactions
|
17 |
+
//
|
18 |
+
// First, populate your database with a tiny sample data set:
|
19 |
+
//
|
20 |
+
// ./redis-cli
|
21 |
+
// SELECT 15
|
22 |
+
// ZADD zset 1 a 2 b 3 c
|
23 |
+
//
|
24 |
+
// Then execute this script four times and see its output.
|
25 |
+
//
|
26 |
+
|
27 |
+
function zpop($client, $key)
|
28 |
+
{
|
29 |
+
$element = null;
|
30 |
+
$options = array(
|
31 |
+
'cas' => true, // Initialize with support for CAS operations
|
32 |
+
'watch' => $key, // Key that needs to be WATCHed to detect changes
|
33 |
+
'retry' => 3, // Number of retries on aborted transactions, after
|
34 |
+
// which the client bails out with an exception.
|
35 |
+
);
|
36 |
+
|
37 |
+
$client->transaction($options, function ($tx) use ($key, &$element) {
|
38 |
+
@list($element) = $tx->zrange($key, 0, 0);
|
39 |
+
|
40 |
+
if (isset($element)) {
|
41 |
+
$tx->multi(); // With CAS, MULTI *must* be explicitly invoked.
|
42 |
+
$tx->zrem($key, $element);
|
43 |
+
}
|
44 |
+
});
|
45 |
+
|
46 |
+
return $element;
|
47 |
+
}
|
48 |
+
|
49 |
+
$client = new Predis\Client($single_server);
|
50 |
+
$zpopped = zpop($client, 'zset');
|
51 |
+
|
52 |
+
echo isset($zpopped) ? "ZPOPed $zpopped" : 'Nothing to ZPOP!', PHP_EOL;
|
dependencies/vendor/predis/predis/package.ini
ADDED
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
; This file is meant to be used with Onion http://c9s.github.com/Onion/
|
2 |
+
; For instructions on how to build a PEAR package of Predis please follow
|
3 |
+
; the instructions at this URL:
|
4 |
+
;
|
5 |
+
; https://github.com/c9s/Onion#a-quick-tutorial-for-building-pear-package
|
6 |
+
;
|
7 |
+
|
8 |
+
[package]
|
9 |
+
name = "Predis"
|
10 |
+
desc = "Flexible and feature-complete Redis client for PHP and HHVM"
|
11 |
+
homepage = "http://github.com/nrk/predis"
|
12 |
+
license = "MIT"
|
13 |
+
version = "1.1.1"
|
14 |
+
stability = "stable"
|
15 |
+
channel = "pear.nrk.io"
|
16 |
+
|
17 |
+
author = "Daniele Alessandri \"nrk\" <suppakilla@gmail.com>"
|
18 |
+
|
19 |
+
[require]
|
20 |
+
php = ">= 5.3.9"
|
21 |
+
pearinstaller = "1.4.1"
|
22 |
+
|
23 |
+
[roles]
|
24 |
+
*.xml.dist = test
|
25 |
+
*.md = doc
|
26 |
+
LICENSE = doc
|
27 |
+
|
28 |
+
[optional phpiredis]
|
29 |
+
hint = "Add support for faster protocol handling with phpiredis"
|
30 |
+
extensions[] = socket
|
31 |
+
extensions[] = phpiredis
|
32 |
+
|
33 |
+
[optional webdis]
|
34 |
+
hint = "Add support for Webdis"
|
35 |
+
extensions[] = curl
|
36 |
+
extensions[] = phpiredis
|
{includes → dependencies/vendor/predis}/predis/src/Autoloader.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Client.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/ClientContextInterface.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/ClientException.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/ClientInterface.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Cluster/ClusterStrategy.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Cluster/Distributor/DistributorInterface.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Cluster/Distributor/EmptyRingException.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Cluster/Distributor/HashRing.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Cluster/Distributor/KetamaRing.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Cluster/Hash/CRC16.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Cluster/Hash/HashGeneratorInterface.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Cluster/PredisStrategy.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Cluster/RedisStrategy.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Cluster/StrategyInterface.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Collection/Iterator/CursorBasedIterator.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Collection/Iterator/HashKey.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Collection/Iterator/Keyspace.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Collection/Iterator/ListKey.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Collection/Iterator/SetKey.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Collection/Iterator/SortedSetKey.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/Command.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/CommandInterface.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ConnectionAuth.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ConnectionEcho.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ConnectionPing.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ConnectionQuit.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ConnectionSelect.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/GeospatialGeoAdd.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/GeospatialGeoDist.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/GeospatialGeoHash.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/GeospatialGeoPos.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/GeospatialGeoRadius.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/GeospatialGeoRadiusByMember.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/HashDelete.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/HashExists.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/HashGet.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/HashGetAll.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/HashGetMultiple.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/HashIncrementBy.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/HashIncrementByFloat.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/HashKeys.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/HashLength.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/HashScan.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/HashSet.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/HashSetMultiple.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/HashSetPreserve.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/HashStringLength.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/HashValues.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/HyperLogLogAdd.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/HyperLogLogCount.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/HyperLogLogMerge.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/KeyDelete.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/KeyDump.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/KeyExists.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/KeyExpire.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/KeyExpireAt.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/KeyKeys.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/KeyMigrate.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/KeyMove.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/KeyPersist.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/KeyPreciseExpire.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/KeyPreciseExpireAt.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/KeyPreciseTimeToLive.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/KeyRandom.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/KeyRename.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/KeyRenamePreserve.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/KeyRestore.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/KeyScan.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/KeySort.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/KeyTimeToLive.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/KeyType.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ListIndex.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ListInsert.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ListLength.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ListPopFirst.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ListPopFirstBlocking.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ListPopLast.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ListPopLastBlocking.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ListPopLastPushHead.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ListPopLastPushHeadBlocking.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ListPushHead.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ListPushHeadX.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ListPushTail.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ListPushTailX.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ListRange.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ListRemove.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ListSet.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ListTrim.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/PrefixableCommandInterface.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/Processor/KeyPrefixProcessor.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/Processor/ProcessorChain.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/Processor/ProcessorInterface.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/PubSubPublish.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/PubSubPubsub.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/PubSubSubscribe.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/PubSubSubscribeByPattern.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/PubSubUnsubscribe.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/PubSubUnsubscribeByPattern.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/RawCommand.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ScriptCommand.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ServerBackgroundRewriteAOF.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ServerBackgroundSave.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ServerClient.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ServerCommand.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ServerConfig.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ServerDatabaseSize.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ServerEval.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ServerEvalSHA.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ServerFlushAll.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ServerFlushDatabase.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ServerInfo.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ServerInfoV26x.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ServerLastSave.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ServerMonitor.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ServerObject.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ServerSave.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ServerScript.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ServerSentinel.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ServerShutdown.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ServerSlaveOf.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ServerSlowlog.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ServerTime.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/SetAdd.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/SetCardinality.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/SetDifference.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/SetDifferenceStore.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/SetIntersection.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/SetIntersectionStore.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/SetIsMember.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/SetMembers.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/SetMove.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/SetPop.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/SetRandomMember.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/SetRemove.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/SetScan.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/SetUnion.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/SetUnionStore.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/StringAppend.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/StringBitCount.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/StringBitField.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/StringBitOp.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/StringBitPos.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/StringDecrement.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/StringDecrementBy.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/StringGet.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/StringGetBit.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/StringGetMultiple.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/StringGetRange.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/StringGetSet.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/StringIncrement.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/StringIncrementBy.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/StringIncrementByFloat.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/StringPreciseSetExpire.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/StringSet.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/StringSetBit.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/StringSetExpire.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/StringSetMultiple.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/StringSetMultiplePreserve.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/StringSetPreserve.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/StringSetRange.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/StringStrlen.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/StringSubstr.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/TransactionDiscard.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/TransactionExec.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/TransactionMulti.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/TransactionUnwatch.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/TransactionWatch.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ZSetAdd.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ZSetCardinality.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ZSetCount.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ZSetIncrementBy.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ZSetIntersectionStore.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ZSetLexCount.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ZSetRange.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ZSetRangeByLex.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ZSetRangeByScore.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ZSetRank.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ZSetRemove.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ZSetRemoveRangeByLex.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ZSetRemoveRangeByRank.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ZSetRemoveRangeByScore.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ZSetReverseRange.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ZSetReverseRangeByLex.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ZSetReverseRangeByScore.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ZSetReverseRank.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ZSetScan.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ZSetScore.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Command/ZSetUnionStore.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/CommunicationException.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Configuration/ClusterOption.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Configuration/ConnectionFactoryOption.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Configuration/ExceptionsOption.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Configuration/OptionInterface.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Configuration/Options.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Configuration/OptionsInterface.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Configuration/PrefixOption.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Configuration/ProfileOption.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Configuration/ReplicationOption.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Connection/AbstractConnection.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Connection/Aggregate/ClusterInterface.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Connection/Aggregate/MasterSlaveReplication.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Connection/Aggregate/PredisCluster.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Connection/Aggregate/RedisCluster.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Connection/Aggregate/ReplicationInterface.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Connection/Aggregate/SentinelReplication.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Connection/AggregateConnectionInterface.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Connection/CompositeConnectionInterface.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Connection/CompositeStreamConnection.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Connection/ConnectionException.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Connection/ConnectionInterface.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Connection/Factory.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Connection/FactoryInterface.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Connection/NodeConnectionInterface.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Connection/Parameters.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Connection/ParametersInterface.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Connection/PhpiredisSocketConnection.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Connection/PhpiredisStreamConnection.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Connection/StreamConnection.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Connection/WebdisConnection.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Monitor/Consumer.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/NotSupportedException.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Pipeline/Atomic.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Pipeline/ConnectionErrorProof.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Pipeline/FireAndForget.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Pipeline/Pipeline.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/PredisException.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Profile/Factory.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Profile/ProfileInterface.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Profile/RedisProfile.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Profile/RedisUnstable.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Profile/RedisVersion200.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Profile/RedisVersion220.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Profile/RedisVersion240.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Profile/RedisVersion260.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Profile/RedisVersion280.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Profile/RedisVersion300.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Profile/RedisVersion320.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Protocol/ProtocolException.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Protocol/ProtocolProcessorInterface.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Protocol/RequestSerializerInterface.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Protocol/ResponseReaderInterface.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Protocol/Text/CompositeProtocolProcessor.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Protocol/Text/Handler/BulkResponse.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Protocol/Text/Handler/ErrorResponse.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Protocol/Text/Handler/IntegerResponse.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Protocol/Text/Handler/MultiBulkResponse.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Protocol/Text/Handler/ResponseHandlerInterface.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Protocol/Text/Handler/StatusResponse.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Protocol/Text/Handler/StreamableMultiBulkResponse.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Protocol/Text/ProtocolProcessor.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Protocol/Text/RequestSerializer.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Protocol/Text/ResponseReader.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/PubSub/AbstractConsumer.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/PubSub/Consumer.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/PubSub/DispatcherLoop.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Replication/MissingMasterException.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Replication/ReplicationStrategy.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Replication/RoleException.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Response/Error.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Response/ErrorInterface.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Response/Iterator/MultiBulk.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Response/Iterator/MultiBulkIterator.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Response/Iterator/MultiBulkTuple.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Response/ResponseInterface.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Response/ServerException.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Response/Status.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Session/Handler.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Transaction/AbortedMultiExecException.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Transaction/MultiExec.php
RENAMED
File without changes
|
{includes → dependencies/vendor/predis}/predis/src/Transaction/MultiExecState.php
RENAMED
File without changes
|
includes/admin-page.css
DELETED
@@ -1,32 +0,0 @@
|
|
1 |
-
|
2 |
-
.settings_page_redis-cache .form-table th,
|
3 |
-
.settings_page_redis-cache .form-table td {
|
4 |
-
padding-top: 7px;
|
5 |
-
padding-bottom: 7px;
|
6 |
-
}
|
7 |
-
|
8 |
-
.settings_page_redis-cache .card,
|
9 |
-
.settings_page_redis-cache br.clearfix {
|
10 |
-
display: none;
|
11 |
-
}
|
12 |
-
|
13 |
-
.settings_page_redis-cache .card ul {
|
14 |
-
list-style: circle;
|
15 |
-
padding-left: 25px;
|
16 |
-
}
|
17 |
-
|
18 |
-
@media screen and (min-width: 1110px) {
|
19 |
-
.settings_page_redis-cache .card {
|
20 |
-
display: block;
|
21 |
-
float: right;
|
22 |
-
}
|
23 |
-
|
24 |
-
.settings_page_redis-cache .section-overview {
|
25 |
-
float: left;
|
26 |
-
}
|
27 |
-
|
28 |
-
.settings_page_redis-cache br.clearfix {
|
29 |
-
display: block;
|
30 |
-
clear: both;
|
31 |
-
}
|
32 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
includes/admin-page.js
DELETED
@@ -1,16 +0,0 @@
|
|
1 |
-
( function ( $ ) {
|
2 |
-
$( function () {
|
3 |
-
$( ".notice.is-dismissible[data-dismissible]" ).on(
|
4 |
-
"click.roc-dismiss-notice",
|
5 |
-
".notice-dismiss",
|
6 |
-
function ( event ) {
|
7 |
-
$.post( ajaxurl, {
|
8 |
-
notice: $( this ).parent().attr( "data-dismissible" ),
|
9 |
-
action: "roc_dismiss_notice",
|
10 |
-
} );
|
11 |
-
|
12 |
-
event.preventDefault();
|
13 |
-
}
|
14 |
-
);
|
15 |
-
} );
|
16 |
-
} ( jQuery ) );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
includes/admin-page.php
DELETED
@@ -1,174 +0,0 @@
|
|
1 |
-
|
2 |
-
<?php if ( ! defined( 'ABSPATH' ) ) exit; ?>
|
3 |
-
|
4 |
-
<div class="wrap">
|
5 |
-
|
6 |
-
<h1><?php _e( 'Redis Object Cache', 'redis-cache' ); ?></h1>
|
7 |
-
|
8 |
-
<?php if ( ! defined( 'WP_REDIS_DISABLE_BANNERS' ) || ! WP_REDIS_DISABLE_BANNERS ) : ?>
|
9 |
-
<div class="card">
|
10 |
-
<h2 class="title">
|
11 |
-
<?php _e( 'Redis Cache Pro', 'redis-cache' ); ?>
|
12 |
-
</h2>
|
13 |
-
<p>
|
14 |
-
<?php _e( '<b>A business class object cache backend.</b> Truly reliable, highly-optimized and fully customizable, with a <u>dedicated engineer</u> when you most need it.', 'redis-cache' ); ?>
|
15 |
-
</p>
|
16 |
-
<ul>
|
17 |
-
<li>Rewritten for raw performance</li>
|
18 |
-
<li>WordPress object cache API compliant</li>
|
19 |
-
<li>Easy debugging & logging</li>
|
20 |
-
<li>Cache analytics and preloading</li>
|
21 |
-
<li>Fully unit tested (100% code coverage)</li>
|
22 |
-
<li>Secure connections with TLS</li>
|
23 |
-
<li>Health checks via WordPress, WP CLI & Debug Bar</li>
|
24 |
-
<li>Optimized for WooCommerce, Jetpack & Yoast SEO</li>
|
25 |
-
</ul>
|
26 |
-
<p>
|
27 |
-
<a class="button button-primary" target="_blank" rel="noopener" href="https://wprediscache.com/?utm_source=wp-plugin&utm_medium=settings">
|
28 |
-
<?php _e( 'Learn more', 'redis-cache' ); ?>
|
29 |
-
</a>
|
30 |
-
</p>
|
31 |
-
</div>
|
32 |
-
<?php endif; ?>
|
33 |
-
|
34 |
-
<div class="section-overview">
|
35 |
-
|
36 |
-
<h2 class="title"><?php _e( 'Overview', 'redis-cache' ); ?></h2>
|
37 |
-
|
38 |
-
<table class="form-table">
|
39 |
-
|
40 |
-
<tr>
|
41 |
-
<th><?php _e( 'Status:', 'redis-cache' ); ?></th>
|
42 |
-
<td><code><?php echo $this->get_status(); ?></code></td>
|
43 |
-
</tr>
|
44 |
-
|
45 |
-
<?php $redisClient = $this->get_redis_client_name(); ?>
|
46 |
-
<?php $redisPrefix = $this->get_redis_cachekey_prefix(); ?>
|
47 |
-
<?php $redisMaxTTL = $this->get_redis_maxttl(); ?>
|
48 |
-
|
49 |
-
<?php if ( ! is_null( $redisClient ) ) : ?>
|
50 |
-
<tr>
|
51 |
-
<th><?php _e( 'Client:', 'redis-cache' ); ?></th>
|
52 |
-
<td>
|
53 |
-
<code><?php echo esc_html( $redisClient ); ?></code>
|
54 |
-
|
55 |
-
<?php if ( strpos( (string) $redisClient, 'predis' ) !== false ) : ?>
|
56 |
-
<p class="description" style="color: #d54e21; max-width: 20rem;">
|
57 |
-
<?php _e( 'The Predis library is no longer maintained. Consider switching over to PhpRedis to avoid compatiblity issues in the future.', 'redis-cache' ); ?>
|
58 |
-
</p>
|
59 |
-
<?php endif; ?>
|
60 |
-
</td>
|
61 |
-
</tr>
|
62 |
-
<?php endif; ?>
|
63 |
-
|
64 |
-
<?php if ( ! is_null( $redisPrefix ) && trim( $redisPrefix ) !== '' ) : ?>
|
65 |
-
<tr>
|
66 |
-
<th><?php _e( 'Key Prefix:', 'redis-cache' ); ?></th>
|
67 |
-
<td>
|
68 |
-
<code><?php echo esc_html( $redisPrefix ); ?></code>
|
69 |
-
</td>
|
70 |
-
</tr>
|
71 |
-
<?php endif; ?>
|
72 |
-
|
73 |
-
<?php if ( ! is_null( $redisMaxTTL ) ) : ?>
|
74 |
-
<tr>
|
75 |
-
<th><?php _e( 'Max. TTL:', 'redis-cache' ); ?></th>
|
76 |
-
<td>
|
77 |
-
<code><?php echo esc_html( $redisMaxTTL ); ?></code>
|
78 |
-
|
79 |
-
<?php if ( ! is_int( $redisMaxTTL ) && ! ctype_digit( $redisMaxTTL ) ) : ?>
|
80 |
-
<p class="description" style="color: #d54e21;">
|
81 |
-
<?php _e( 'This doesn’t appear to be a valid number.', 'redis-cache' ); ?>
|
82 |
-
</p>
|
83 |
-
<?php endif; ?>
|
84 |
-
</td>
|
85 |
-
</tr>
|
86 |
-
<?php endif; ?>
|
87 |
-
|
88 |
-
</table>
|
89 |
-
|
90 |
-
<p class="submit">
|
91 |
-
|
92 |
-
<?php if ( $this->get_redis_status() ) : ?>
|
93 |
-
<a href="<?php echo wp_nonce_url( network_admin_url( add_query_arg( 'action', 'flush-cache', $this->page ) ), 'flush-cache' ); ?>" class="button button-primary button-large"><?php _e( 'Flush Cache', 'redis-cache' ); ?></a>
|
94 |
-
<?php endif; ?>
|
95 |
-
|
96 |
-
<?php if ( ! $this->object_cache_dropin_exists() ) : ?>
|
97 |
-
<a href="<?php echo wp_nonce_url( network_admin_url( add_query_arg( 'action', 'enable-cache', $this->page ) ), 'enable-cache' ); ?>" class="button button-primary button-large"><?php _e( 'Enable Object Cache', 'redis-cache' ); ?></a>
|
98 |
-
<?php elseif ( $this->validate_object_cache_dropin() ) : ?>
|
99 |
-
<a href="<?php echo wp_nonce_url( network_admin_url( add_query_arg( 'action', 'disable-cache', $this->page ) ), 'disable-cache' ); ?>" class="button button-secondary button-large"><?php _e( 'Disable Object Cache', 'redis-cache' ); ?></a>
|
100 |
-
<?php endif; ?>
|
101 |
-
|
102 |
-
</p>
|
103 |
-
|
104 |
-
</div>
|
105 |
-
|
106 |
-
<br class="clearfix">
|
107 |
-
|
108 |
-
<h2 class="title">
|
109 |
-
<?php _e( 'Servers', 'redis-cache' ); ?>
|
110 |
-
</h2>
|
111 |
-
|
112 |
-
<?php $this->show_servers_list(); ?>
|
113 |
-
|
114 |
-
<?php if ( ! defined( 'WP_REDIS_DISABLE_BANNERS' ) || ! WP_REDIS_DISABLE_BANNERS ) : ?>
|
115 |
-
<br>
|
116 |
-
<h2 class="title">
|
117 |
-
<?php _e( 'Redis Cache Pro', 'redis-cache' ); ?>
|
118 |
-
</h2>
|
119 |
-
|
120 |
-
<?php $isPhp7 = version_compare( phpversion(), '7.0', '>=' ); ?>
|
121 |
-
<?php $isPhpRedis311 = version_compare( phpversion( 'redis' ), '3.1.1', '>=' ); ?>
|
122 |
-
<?php $phpRedisInstalled = (bool) phpversion( 'redis' ); ?>
|
123 |
-
|
124 |
-
<?php if ($isPhp7 && $isPhpRedis311) : ?>
|
125 |
-
<p>
|
126 |
-
<?php _e( 'Your site meets the system requirements for the Pro version (PHP 7+; PhpRedis 3.1.1+).', 'redis-cache' ); ?>
|
127 |
-
</p>
|
128 |
-
<?php else : ?>
|
129 |
-
<p>
|
130 |
-
<?php _e( 'Your site <i>does not</i> meet the system requirements for the Pro version:', 'redis-cache' ); ?>
|
131 |
-
</p>
|
132 |
-
|
133 |
-
<ul style="padding-left: 30px; list-style: disc;">
|
134 |
-
<?php if (! $isPhp7) : ?>
|
135 |
-
<li>
|
136 |
-
<?php printf( __( 'The current version (%s) of PHP is too old. PHP 7 or newer is required.', 'redis-cache' ), phpversion() ); ?>
|
137 |
-
</li>
|
138 |
-
<?php endif; ?>
|
139 |
-
|
140 |
-
<?php if (! $phpRedisInstalled) : ?>
|
141 |
-
<li>
|
142 |
-
<?php printf( __( 'The PhpRedis extension is not installed.', 'redis-cache' ), phpversion() ); ?>
|
143 |
-
</li>
|
144 |
-
<?php elseif (! $isPhpRedis311) : ?>
|
145 |
-
<li>
|
146 |
-
<?php printf( __( 'The current version (%s) of the PhpRedis extension is too old. PhpRedis 3.1 or newer is required.', 'redis-cache' ), phpversion( 'redis' ) ); ?>
|
147 |
-
</li>
|
148 |
-
<?php endif; ?>
|
149 |
-
</ul>
|
150 |
-
|
151 |
-
<?php endif; ?>
|
152 |
-
<?php endif; ?>
|
153 |
-
|
154 |
-
<?php if ( isset( $_GET[ 'diagnostics' ] ) ) : ?>
|
155 |
-
|
156 |
-
<br>
|
157 |
-
<h2 class="title">
|
158 |
-
<?php _e( 'Diagnostics', 'redis-cache' ); ?>
|
159 |
-
</h2>
|
160 |
-
|
161 |
-
<textarea class="large-text readonly" rows="20" readonly><?php include dirname( __FILE__ ) . '/diagnostics.php'; ?></textarea>
|
162 |
-
|
163 |
-
<?php else : ?>
|
164 |
-
|
165 |
-
<p class="mt-5">
|
166 |
-
<br>
|
167 |
-
<a class="button button-secondary" href="<?php echo network_admin_url( add_query_arg( 'diagnostics', '1', $this->page ) ); ?>">
|
168 |
-
<?php _e( 'Show Diagnostics', 'redis-cache' ); ?>
|
169 |
-
</a>
|
170 |
-
</p>
|
171 |
-
|
172 |
-
<?php endif; ?>
|
173 |
-
|
174 |
-
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
includes/class-autoloader.php
ADDED
@@ -0,0 +1,145 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Autoloader class adhering to WordPress naming scheme.
|
4 |
+
*
|
5 |
+
* @package Rhubarb\RedisCache
|
6 |
+
*/
|
7 |
+
|
8 |
+
namespace Rhubarb\RedisCache;
|
9 |
+
|
10 |
+
defined( '\\ABSPATH' ) || exit;
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Autoloader class
|
14 |
+
*/
|
15 |
+
class Autoloader {
|
16 |
+
/**
|
17 |
+
* An associative array where the key is a namespace prefix and the value
|
18 |
+
* is an array of base directories for classes in that namespace.
|
19 |
+
*
|
20 |
+
* @var array
|
21 |
+
*/
|
22 |
+
private $prefixes = [];
|
23 |
+
|
24 |
+
/**
|
25 |
+
* Prefix to the class files to adhere to WordPress coding guidelines.
|
26 |
+
*
|
27 |
+
* @var string
|
28 |
+
*/
|
29 |
+
private $class_file_prefix = 'class-';
|
30 |
+
|
31 |
+
/**
|
32 |
+
* Register loader with SPL autoloader stack.
|
33 |
+
*
|
34 |
+
* @since 2.0.0
|
35 |
+
* @return void
|
36 |
+
*/
|
37 |
+
public function register() {
|
38 |
+
spl_autoload_register( [ $this, 'load_class' ] );
|
39 |
+
}
|
40 |
+
|
41 |
+
/**
|
42 |
+
* Adds a base directory for a namespace prefix.
|
43 |
+
*
|
44 |
+
* @since 2.0.0
|
45 |
+
* @param string $prefix The namespace prefix.
|
46 |
+
* @param string $base_dir A base directory for class files in the
|
47 |
+
* namespace.
|
48 |
+
* @param bool $prepend If true, prepend the base directory to the stack
|
49 |
+
* instead of appending it; this causes it to be
|
50 |
+
* searched first rather than last.
|
51 |
+
* @return void
|
52 |
+
*/
|
53 |
+
public function add_namespace( $prefix, $base_dir, $prepend = false ) {
|
54 |
+
$prefix = trim( $prefix, '\\' ) . '\\';
|
55 |
+
|
56 |
+
$base_dir = rtrim( $base_dir, \DIRECTORY_SEPARATOR ) . '/';
|
57 |
+
|
58 |
+
if ( false === isset( $this->prefixes[ $prefix ] ) ) {
|
59 |
+
$this->prefixes[ $prefix ] = [];
|
60 |
+
}
|
61 |
+
|
62 |
+
if ( $prepend ) {
|
63 |
+
array_unshift( $this->prefixes[ $prefix ], $base_dir );
|
64 |
+
} else {
|
65 |
+
array_push( $this->prefixes[ $prefix ], $base_dir );
|
66 |
+
}
|
67 |
+
}
|
68 |
+
|
69 |
+
/**
|
70 |
+
* Loads the class file for a given class name.
|
71 |
+
*
|
72 |
+
* @since 2.0.0
|
73 |
+
* @param string $class The fully-qualified class name.
|
74 |
+
* @return string|null The mapped file name on success, or null on failure.
|
75 |
+
*/
|
76 |
+
public function load_class( $class ) {
|
77 |
+
$prefix = $class;
|
78 |
+
|
79 |
+
while ( false !== ( $pos = strrpos( $prefix, '\\' ) ) ) { // phpcs:ignore
|
80 |
+
$prefix = substr( $class, 0, $pos + 1 );
|
81 |
+
|
82 |
+
$relative_class = substr( $class, $pos + 1 );
|
83 |
+
|
84 |
+
$mapped_file = $this->load_mapped_file( $prefix, $relative_class );
|
85 |
+
if ( $mapped_file ) {
|
86 |
+
return $mapped_file;
|
87 |
+
}
|
88 |
+
|
89 |
+
$prefix = rtrim( $prefix, '\\' );
|
90 |
+
}
|
91 |
+
|
92 |
+
return null;
|
93 |
+
}
|
94 |
+
|
95 |
+
/**
|
96 |
+
* Load the mapped file for a namespace prefix and relative class.
|
97 |
+
*
|
98 |
+
* @since 2.0.0
|
99 |
+
* @param string $prefix The namespace prefix.
|
100 |
+
* @param string $relative_class The relative class name.
|
101 |
+
* @return string|null Null if no mapped file can be loaded, or
|
102 |
+
* the name of the loaded mapped file.
|
103 |
+
*/
|
104 |
+
private function load_mapped_file( $prefix, $relative_class ) {
|
105 |
+
if ( false === isset( $this->prefixes[ $prefix ] ) ) {
|
106 |
+
return null;
|
107 |
+
}
|
108 |
+
|
109 |
+
foreach ( $this->prefixes[ $prefix ] as $base_dir ) {
|
110 |
+
$relative_class = strtolower( $relative_class );
|
111 |
+
$relative_class = strtr( $relative_class, '_', '-' );
|
112 |
+
|
113 |
+
$file = $base_dir
|
114 |
+
. str_replace( '\\', '/', $relative_class )
|
115 |
+
. '.php';
|
116 |
+
|
117 |
+
if ( $this->class_file_prefix ) {
|
118 |
+
$pos = strrpos( $file, '/' );
|
119 |
+
$filename = $this->class_file_prefix . substr( $file, $pos + 1 );
|
120 |
+
$file = substr_replace( $file, $filename, $pos + 1 );
|
121 |
+
}
|
122 |
+
|
123 |
+
if ( $this->require_file( $file ) ) {
|
124 |
+
return $file;
|
125 |
+
}
|
126 |
+
}
|
127 |
+
|
128 |
+
return null;
|
129 |
+
}
|
130 |
+
|
131 |
+
/**
|
132 |
+
* If a file exists, require it from the file system.
|
133 |
+
*
|
134 |
+
* @since 2.0.0
|
135 |
+
* @param string $file The file to require.
|
136 |
+
* @return bool True if the file exists, false if not.
|
137 |
+
*/
|
138 |
+
private function require_file( $file ) {
|
139 |
+
if ( file_exists( $file ) ) {
|
140 |
+
require $file;
|
141 |
+
return true;
|
142 |
+
}
|
143 |
+
return false;
|
144 |
+
}
|
145 |
+
}
|
includes/class-plugin.php
ADDED
@@ -0,0 +1,721 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Main plugin class
|
4 |
+
*
|
5 |
+
* @package Rhubarb\RedisCache
|
6 |
+
*/
|
7 |
+
|
8 |
+
namespace Rhubarb\RedisCache;
|
9 |
+
|
10 |
+
defined( '\\ABSPATH' ) || exit;
|
11 |
+
|
12 |
+
class Plugin {
|
13 |
+
|
14 |
+
private $page;
|
15 |
+
|
16 |
+
private $screen = 'settings_page_redis-cache';
|
17 |
+
|
18 |
+
private $actions = array(
|
19 |
+
'enable-cache',
|
20 |
+
'disable-cache',
|
21 |
+
'flush-cache',
|
22 |
+
'update-dropin',
|
23 |
+
);
|
24 |
+
|
25 |
+
/**
|
26 |
+
* Plugin instance property.
|
27 |
+
*
|
28 |
+
* @var Plugin
|
29 |
+
*/
|
30 |
+
private static $instance;
|
31 |
+
|
32 |
+
private function __construct() {
|
33 |
+
require_once ABSPATH . 'wp-admin/includes/plugin.php';
|
34 |
+
|
35 |
+
load_plugin_textdomain( 'redis-cache', false, 'redis-cache/languages' );
|
36 |
+
register_activation_hook( WP_REDIS_FILE, 'wp_cache_flush' );
|
37 |
+
|
38 |
+
$this->page = is_multisite() ? 'settings.php?page=redis-cache' : 'options-general.php?page=redis-cache';
|
39 |
+
|
40 |
+
add_action( 'deactivate_plugin', array( $this, 'on_deactivation' ) );
|
41 |
+
add_action( 'upgrader_process_complete', array( $this, 'maybe_update_dropin' ), 10, 2 );
|
42 |
+
|
43 |
+
add_action( is_multisite() ? 'network_admin_menu' : 'admin_menu', array( $this, 'add_admin_menu_page' ) );
|
44 |
+
|
45 |
+
add_action( 'admin_notices', array( $this, 'show_admin_notices' ) );
|
46 |
+
add_action( 'network_admin_notices', array( $this, 'show_admin_notices' ) );
|
47 |
+
|
48 |
+
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_styles' ) );
|
49 |
+
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_scripts' ) );
|
50 |
+
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_redis_metrics' ) );
|
51 |
+
|
52 |
+
add_action( 'load-' . $this->screen, array( $this, 'do_admin_actions' ) );
|
53 |
+
add_action( 'load-' . $this->screen, array( $this, 'add_admin_page_notices' ) );
|
54 |
+
|
55 |
+
add_action( 'wp_dashboard_setup', array( $this, 'setup_dashboard_widget' ) );
|
56 |
+
add_action( 'wp_network_dashboard_setup', array( $this, 'setup_dashboard_widget' ) );
|
57 |
+
|
58 |
+
add_action( 'wp_ajax_roc_dismiss_notice', array( $this, 'dismiss_notice' ) );
|
59 |
+
|
60 |
+
$links = sprintf( '%splugin_action_links_%s', is_multisite() ? 'network_admin_' : '', WP_REDIS_BASENAME );
|
61 |
+
add_filter( $links, array( $this, 'add_plugin_actions_links' ) );
|
62 |
+
|
63 |
+
add_action( 'wp_head', array( $this, 'register_shutdown_hooks' ) );
|
64 |
+
add_action( 'shutdown', array( $this, 'record_metrics' ) );
|
65 |
+
add_action( 'rediscache_discard_metrics', array( $this, 'discard_metrics' ) );
|
66 |
+
|
67 |
+
add_filter( 'qm/collectors', array( $this, 'register_qm_collector' ), 25 );
|
68 |
+
add_filter( 'qm/outputter/html', array( $this, 'register_qm_output' ) );
|
69 |
+
|
70 |
+
if ( is_admin() && ! wp_next_scheduled( 'rediscache_discard_metrics' ) ) {
|
71 |
+
wp_schedule_event( time(), 'hourly', 'rediscache_discard_metrics' );
|
72 |
+
}
|
73 |
+
}
|
74 |
+
|
75 |
+
/**
|
76 |
+
* Plugin instanciation method.
|
77 |
+
*
|
78 |
+
* @return Plugin
|
79 |
+
*/
|
80 |
+
public static function instance() {
|
81 |
+
if ( ! isset( self::$instance ) ) {
|
82 |
+
self::$instance = new self();
|
83 |
+
}
|
84 |
+
|
85 |
+
return self::$instance;
|
86 |
+
}
|
87 |
+
|
88 |
+
public function add_admin_menu_page() {
|
89 |
+
// add sub-page to "Settings"
|
90 |
+
add_submenu_page(
|
91 |
+
is_multisite() ? 'settings.php' : 'options-general.php',
|
92 |
+
__( 'Redis Object Cache', 'redis-cache' ),
|
93 |
+
__( 'Redis', 'redis-cache' ),
|
94 |
+
is_multisite() ? 'manage_network_options' : 'manage_options',
|
95 |
+
'redis-cache',
|
96 |
+
array( $this, 'show_admin_page' )
|
97 |
+
);
|
98 |
+
}
|
99 |
+
|
100 |
+
public function show_admin_page() {
|
101 |
+
// request filesystem credentials?
|
102 |
+
if ( isset( $_GET['_wpnonce'], $_GET['action'] ) ) {
|
103 |
+
$action = $_GET['action'];
|
104 |
+
|
105 |
+
foreach ( $this->actions as $name ) {
|
106 |
+
// verify nonce
|
107 |
+
if ( $action === $name && wp_verify_nonce( $_GET['_wpnonce'], $action ) ) {
|
108 |
+
$url = wp_nonce_url( network_admin_url( add_query_arg( 'action', $action, $this->page ) ), $action );
|
109 |
+
|
110 |
+
if ( $this->initialize_filesystem( $url ) === false ) {
|
111 |
+
return; // request filesystem credentials
|
112 |
+
}
|
113 |
+
}
|
114 |
+
}
|
115 |
+
}
|
116 |
+
|
117 |
+
if ( wp_next_scheduled( 'redis_gather_metrics' ) ) {
|
118 |
+
wp_clear_scheduled_hook( 'redis_gather_metrics' );
|
119 |
+
}
|
120 |
+
|
121 |
+
UI::register_tab( 'overview', __( 'Overview', 'redis-cache' ), [ 'default' => true ] );
|
122 |
+
UI::register_tab( 'metrics', __( 'Metrics', 'redis-cache' ) );
|
123 |
+
UI::register_tab( 'diagnostics', __( 'Diagnostics', 'redis-cache' ) );
|
124 |
+
|
125 |
+
// show admin page
|
126 |
+
require_once WP_REDIS_PLUGIN_PATH . '/includes/ui/settings.php';
|
127 |
+
}
|
128 |
+
|
129 |
+
public function setup_dashboard_widget() {
|
130 |
+
if ( defined( 'WP_REDIS_DISABLE_METRICS' ) && WP_REDIS_DISABLE_METRICS ) {
|
131 |
+
return;
|
132 |
+
}
|
133 |
+
|
134 |
+
wp_add_dashboard_widget(
|
135 |
+
'dashboard_rediscache',
|
136 |
+
__( 'Redis Object Cache', 'redis-cache' ),
|
137 |
+
array( $this, 'show_dashboard_widget' )
|
138 |
+
);
|
139 |
+
}
|
140 |
+
|
141 |
+
public function show_dashboard_widget() {
|
142 |
+
require_once WP_REDIS_PLUGIN_PATH . '/includes/ui/widget.php';
|
143 |
+
}
|
144 |
+
|
145 |
+
public function add_plugin_actions_links( $links ) {
|
146 |
+
// add settings link to plugin actions
|
147 |
+
return array_merge(
|
148 |
+
[ sprintf( '<a href="%s">$s</a>', network_admin_url( $this->page ), esc_html__( 'Settings', 'redis-cache' ) ) ],
|
149 |
+
$links
|
150 |
+
);
|
151 |
+
}
|
152 |
+
|
153 |
+
public function enqueue_admin_styles( $hook_suffix ) {
|
154 |
+
if ( in_array( $hook_suffix, array( 'index.php', $this->screen ) ) ) {
|
155 |
+
wp_enqueue_style( 'redis-cache', WP_REDIS_DIR . '/assets/css/admin.css', null, WP_REDIS_VERSION );
|
156 |
+
}
|
157 |
+
}
|
158 |
+
|
159 |
+
public function enqueue_admin_scripts() {
|
160 |
+
$screen = get_current_screen();
|
161 |
+
|
162 |
+
if ( ! isset( $screen->id ) ) {
|
163 |
+
return;
|
164 |
+
}
|
165 |
+
|
166 |
+
$screens = array(
|
167 |
+
$this->screen,
|
168 |
+
'dashboard',
|
169 |
+
'dashboard-network',
|
170 |
+
'edit-shop_order',
|
171 |
+
'edit-product',
|
172 |
+
'woocommerce_page_wc-admin',
|
173 |
+
);
|
174 |
+
|
175 |
+
if ( ! in_array( $screen->id, $screens ) ) {
|
176 |
+
return;
|
177 |
+
}
|
178 |
+
|
179 |
+
wp_enqueue_script(
|
180 |
+
'redis-cache',
|
181 |
+
plugins_url( 'assets/js/admin.js', WP_REDIS_FILE ),
|
182 |
+
array( 'jquery', 'underscore' ),
|
183 |
+
WP_REDIS_VERSION
|
184 |
+
);
|
185 |
+
|
186 |
+
wp_localize_script(
|
187 |
+
'redis-cache',
|
188 |
+
'rediscache',
|
189 |
+
array(
|
190 |
+
'jQuery' => 'jQuery',
|
191 |
+
'l10n' => array(
|
192 |
+
'time' => __( 'Time', 'redis-cache' ),
|
193 |
+
'bytes' => __( 'Bytes', 'redis-cache' ),
|
194 |
+
'ratio' => __( 'Ratio', 'redis-cache' ),
|
195 |
+
'calls' => __( 'Calls', 'redis-cache' ),
|
196 |
+
'no_data' => __( 'Not enough data collected, yet.', 'redis-cache' ),
|
197 |
+
'no_cache' => __( 'Enable object cache to collect data.', 'redis-cache' ),
|
198 |
+
'pro' => 'Redis Cache Pro',
|
199 |
+
),
|
200 |
+
)
|
201 |
+
);
|
202 |
+
}
|
203 |
+
|
204 |
+
public function enqueue_redis_metrics() {
|
205 |
+
global $wp_object_cache;
|
206 |
+
|
207 |
+
if ( defined( 'WP_REDIS_DISABLE_METRICS' ) && WP_REDIS_DISABLE_METRICS ) {
|
208 |
+
return;
|
209 |
+
}
|
210 |
+
|
211 |
+
$screen = get_current_screen();
|
212 |
+
|
213 |
+
if ( ! isset( $screen->id ) ) {
|
214 |
+
return;
|
215 |
+
}
|
216 |
+
|
217 |
+
if ( ! in_array( $screen->id, array( $this->screen, 'dashboard', 'dashboard-network' ) ) ) {
|
218 |
+
return;
|
219 |
+
}
|
220 |
+
|
221 |
+
wp_enqueue_script(
|
222 |
+
'redis-cache-charts',
|
223 |
+
plugins_url( 'assets/js/apexcharts.min.js', WP_REDIS_FILE ),
|
224 |
+
null,
|
225 |
+
WP_REDIS_VERSION
|
226 |
+
);
|
227 |
+
|
228 |
+
if ( ! method_exists( $wp_object_cache, 'redis_instance' ) ) {
|
229 |
+
return;
|
230 |
+
}
|
231 |
+
|
232 |
+
$metrics = $wp_object_cache->redis_instance()->zrangebyscore(
|
233 |
+
$wp_object_cache->build_key( 'metrics', 'redis-cache' ),
|
234 |
+
time() - ( MINUTE_IN_SECONDS * 30 ),
|
235 |
+
time() - MINUTE_IN_SECONDS,
|
236 |
+
[ 'withscores' => true ]
|
237 |
+
);
|
238 |
+
|
239 |
+
wp_localize_script( 'redis-cache', 'rediscache_metrics', $metrics );
|
240 |
+
}
|
241 |
+
|
242 |
+
public function register_qm_collector( array $collectors ) {
|
243 |
+
$collectors['cache'] = new QM_Collector();
|
244 |
+
|
245 |
+
return $collectors;
|
246 |
+
}
|
247 |
+
|
248 |
+
public function register_qm_output( $output ) {
|
249 |
+
$output['cache'] = new QM_Output( \QM_Collectors::get( 'cache' ) );
|
250 |
+
|
251 |
+
return $output;
|
252 |
+
}
|
253 |
+
|
254 |
+
public function object_cache_dropin_exists() {
|
255 |
+
return file_exists( WP_CONTENT_DIR . '/object-cache.php' );
|
256 |
+
}
|
257 |
+
|
258 |
+
public function validate_object_cache_dropin() {
|
259 |
+
if ( ! $this->object_cache_dropin_exists() ) {
|
260 |
+
return false;
|
261 |
+
}
|
262 |
+
|
263 |
+
$dropin = get_plugin_data( WP_CONTENT_DIR . '/object-cache.php' );
|
264 |
+
$plugin = get_plugin_data( WP_REDIS_PLUGIN_PATH . '/includes/object-cache.php' );
|
265 |
+
|
266 |
+
return ( strcmp( $dropin['PluginURI'], $plugin['PluginURI'] ) === 0 );
|
267 |
+
}
|
268 |
+
|
269 |
+
public function get_status() {
|
270 |
+
if (
|
271 |
+
! $this->object_cache_dropin_exists() ||
|
272 |
+
( defined( 'WP_REDIS_DISABLED' ) && WP_REDIS_DISABLED )
|
273 |
+
) {
|
274 |
+
return __( 'Disabled', 'redis-cache' );
|
275 |
+
}
|
276 |
+
|
277 |
+
if ( $this->validate_object_cache_dropin() ) {
|
278 |
+
if ( $this->get_redis_status() ) {
|
279 |
+
return __( 'Connected', 'redis-cache' );
|
280 |
+
}
|
281 |
+
|
282 |
+
if ( $this->get_redis_status() === false ) {
|
283 |
+
return __( 'Not Connected', 'redis-cache' );
|
284 |
+
}
|
285 |
+
}
|
286 |
+
|
287 |
+
return __( 'Unknown', 'redis-cache' );
|
288 |
+
}
|
289 |
+
|
290 |
+
public function get_redis_status() {
|
291 |
+
global $wp_object_cache;
|
292 |
+
|
293 |
+
if ( defined( 'WP_REDIS_DISABLED' ) && WP_REDIS_DISABLED ) {
|
294 |
+
return;
|
295 |
+
}
|
296 |
+
|
297 |
+
if ( $this->validate_object_cache_dropin() && method_exists( $wp_object_cache, 'redis_status' ) ) {
|
298 |
+
return $wp_object_cache->redis_status();
|
299 |
+
}
|
300 |
+
|
301 |
+
return;
|
302 |
+
}
|
303 |
+
|
304 |
+
public function get_redis_version() {
|
305 |
+
global $wp_object_cache;
|
306 |
+
|
307 |
+
if ( defined( 'WP_REDIS_DISABLED' ) && WP_REDIS_DISABLED ) {
|
308 |
+
return;
|
309 |
+
}
|
310 |
+
|
311 |
+
if ( $this->validate_object_cache_dropin() && method_exists( $wp_object_cache, 'redis_version' ) ) {
|
312 |
+
return $wp_object_cache->redis_version();
|
313 |
+
}
|
314 |
+
}
|
315 |
+
|
316 |
+
public function get_redis_client_name() {
|
317 |
+
global $wp_object_cache;
|
318 |
+
|
319 |
+
if ( isset( $wp_object_cache->diagnostics[ 'client' ] ) ) {
|
320 |
+
return $wp_object_cache->diagnostics[ 'client' ];
|
321 |
+
}
|
322 |
+
|
323 |
+
if ( defined( 'WP_REDIS_CLIENT' ) ) {
|
324 |
+
return WP_REDIS_CLIENT;
|
325 |
+
}
|
326 |
+
}
|
327 |
+
|
328 |
+
public function get_diagnostics() {
|
329 |
+
global $wp_object_cache;
|
330 |
+
|
331 |
+
if ( $this->validate_object_cache_dropin() && property_exists( $wp_object_cache, 'diagnostics' ) ) {
|
332 |
+
return $wp_object_cache->diagnostics;
|
333 |
+
}
|
334 |
+
}
|
335 |
+
|
336 |
+
public function get_redis_prefix() {
|
337 |
+
return defined( 'WP_REDIS_PREFIX' ) ? WP_REDIS_PREFIX : null;
|
338 |
+
}
|
339 |
+
|
340 |
+
public function get_redis_maxttl() {
|
341 |
+
return defined( 'WP_REDIS_MAXTTL' ) ? WP_REDIS_MAXTTL : null;
|
342 |
+
}
|
343 |
+
|
344 |
+
public function show_admin_notices() {
|
345 |
+
if ( ! defined( 'WP_REDIS_DISABLE_BANNERS' ) || ! WP_REDIS_DISABLE_BANNERS ) {
|
346 |
+
$this->pro_notice();
|
347 |
+
$this->wc_pro_notice();
|
348 |
+
}
|
349 |
+
|
350 |
+
// only show admin notices to users with the right capability
|
351 |
+
if ( ! current_user_can( is_multisite() ? 'manage_network_options' : 'manage_options' ) ) {
|
352 |
+
return;
|
353 |
+
}
|
354 |
+
|
355 |
+
if ( $this->object_cache_dropin_exists() ) {
|
356 |
+
|
357 |
+
$url = wp_nonce_url( network_admin_url( add_query_arg( 'action', 'update-dropin', $this->page ) ), 'update-dropin' );
|
358 |
+
|
359 |
+
if ( $this->validate_object_cache_dropin() ) {
|
360 |
+
$dropin = get_plugin_data( WP_CONTENT_DIR . '/object-cache.php' );
|
361 |
+
$plugin = get_plugin_data( WP_REDIS_PLUGIN_PATH . '/includes/object-cache.php' );
|
362 |
+
|
363 |
+
if ( version_compare( $dropin['Version'], $plugin['Version'], '<' ) ) {
|
364 |
+
$message = sprintf( __( 'The Redis object cache drop-in is outdated. Please <a href="%s">update the drop-in</a>.', 'redis-cache' ), $url );
|
365 |
+
}
|
366 |
+
} else {
|
367 |
+
$message = sprintf( __( 'A foreign object cache drop-in was found. To use Redis for object caching, please <a href="%s">enable the drop-in</a>.', 'redis-cache' ), $url );
|
368 |
+
}
|
369 |
+
|
370 |
+
if ( isset( $message ) ) {
|
371 |
+
printf( '<div class="update-nag">%s</div>', $message );
|
372 |
+
}
|
373 |
+
}
|
374 |
+
}
|
375 |
+
|
376 |
+
public function add_admin_page_notices() {
|
377 |
+
// show PHP version warning
|
378 |
+
if ( version_compare( PHP_VERSION, '5.4.0', '<' ) ) {
|
379 |
+
add_settings_error( '', 'redis-cache', __( 'This plugin requires PHP 5.4 or greater.', 'redis-cache' ) );
|
380 |
+
}
|
381 |
+
|
382 |
+
// show action success/failure messages
|
383 |
+
if ( isset( $_GET['message'] ) ) {
|
384 |
+
switch ( $_GET['message'] ) {
|
385 |
+
case 'cache-enabled':
|
386 |
+
$message = __( 'Object cache enabled.', 'redis-cache' );
|
387 |
+
break;
|
388 |
+
case 'enable-cache-failed':
|
389 |
+
$error = __( 'Object cache could not be enabled.', 'redis-cache' );
|
390 |
+
break;
|
391 |
+
case 'cache-disabled':
|
392 |
+
$message = __( 'Object cache disabled.', 'redis-cache' );
|
393 |
+
break;
|
394 |
+
case 'disable-cache-failed':
|
395 |
+
$error = __( 'Object cache could not be disabled.', 'redis-cache' );
|
396 |
+
break;
|
397 |
+
case 'cache-flushed':
|
398 |
+
$message = __( 'Object cache flushed.', 'redis-cache' );
|
399 |
+
break;
|
400 |
+
case 'flush-cache-failed':
|
401 |
+
$error = __( 'Object cache could not be flushed.', 'redis-cache' );
|
402 |
+
break;
|
403 |
+
case 'dropin-updated':
|
404 |
+
$message = __( 'Updated object cache drop-in and enabled Redis object cache.', 'redis-cache' );
|
405 |
+
break;
|
406 |
+
case 'update-dropin-failed':
|
407 |
+
$error = __( 'Object cache drop-in could not be updated.', 'redis-cache' );
|
408 |
+
break;
|
409 |
+
}
|
410 |
+
|
411 |
+
add_settings_error( '', 'redis-cache', isset( $message ) ? $message : $error, isset( $message ) ? 'updated' : 'error' );
|
412 |
+
}
|
413 |
+
}
|
414 |
+
|
415 |
+
public function do_admin_actions() {
|
416 |
+
global $wp_filesystem;
|
417 |
+
|
418 |
+
if ( isset( $_GET['_wpnonce'], $_GET['action'] ) ) {
|
419 |
+
$action = $_GET['action'];
|
420 |
+
|
421 |
+
// verify nonce
|
422 |
+
foreach ( $this->actions as $name ) {
|
423 |
+
if ( $action === $name && ! wp_verify_nonce( $_GET['_wpnonce'], $action ) ) {
|
424 |
+
return;
|
425 |
+
}
|
426 |
+
}
|
427 |
+
|
428 |
+
if ( in_array( $action, $this->actions ) ) {
|
429 |
+
$url = wp_nonce_url( network_admin_url( add_query_arg( 'action', $action, $this->page ) ), $action );
|
430 |
+
|
431 |
+
if ( $action === 'flush-cache' ) {
|
432 |
+
$message = wp_cache_flush() ? 'cache-flushed' : 'flush-cache-failed';
|
433 |
+
}
|
434 |
+
|
435 |
+
// do we have filesystem credentials?
|
436 |
+
if ( $this->initialize_filesystem( $url, true ) ) {
|
437 |
+
switch ( $action ) {
|
438 |
+
case 'enable-cache':
|
439 |
+
$result = $wp_filesystem->copy( WP_REDIS_PLUGIN_PATH . '/includes/object-cache.php', WP_CONTENT_DIR . '/object-cache.php', true );
|
440 |
+
do_action( 'redis_object_cache_enable', $result );
|
441 |
+
$message = $result ? 'cache-enabled' : 'enable-cache-failed';
|
442 |
+
break;
|
443 |
+
|
444 |
+
case 'disable-cache':
|
445 |
+
$result = $wp_filesystem->delete( WP_CONTENT_DIR . '/object-cache.php' );
|
446 |
+
do_action( 'redis_object_cache_disable', $result );
|
447 |
+
$message = $result ? 'cache-disabled' : 'disable-cache-failed';
|
448 |
+
break;
|
449 |
+
|
450 |
+
case 'update-dropin':
|
451 |
+
$result = $wp_filesystem->copy( WP_REDIS_PLUGIN_PATH . '/includes/object-cache.php', WP_CONTENT_DIR . '/object-cache.php', true );
|
452 |
+
do_action( 'redis_object_cache_update_dropin', $result );
|
453 |
+
$message = $result ? 'dropin-updated' : 'update-dropin-failed';
|
454 |
+
break;
|
455 |
+
}
|
456 |
+
}
|
457 |
+
|
458 |
+
// redirect if status `$message` was set
|
459 |
+
if ( isset( $message ) ) {
|
460 |
+
wp_safe_redirect( network_admin_url( add_query_arg( 'message', $message, $this->page ) ) );
|
461 |
+
exit;
|
462 |
+
}
|
463 |
+
}
|
464 |
+
}
|
465 |
+
}
|
466 |
+
|
467 |
+
public function dismiss_notice() {
|
468 |
+
$notice = sprintf(
|
469 |
+
'roc_dismissed_%s',
|
470 |
+
sanitize_key( $_POST['notice'] )
|
471 |
+
);
|
472 |
+
|
473 |
+
update_user_meta( get_current_user_id(), $notice, '1' );
|
474 |
+
|
475 |
+
wp_die();
|
476 |
+
}
|
477 |
+
|
478 |
+
public function pro_notice() {
|
479 |
+
$screen = get_current_screen();
|
480 |
+
|
481 |
+
if ( ! isset( $screen->id ) ) {
|
482 |
+
return;
|
483 |
+
}
|
484 |
+
|
485 |
+
if ( ! in_array( $screen->id, array( 'dashboard', 'dashboard-network' ) ) ) {
|
486 |
+
return;
|
487 |
+
}
|
488 |
+
|
489 |
+
if ( ! current_user_can( 'manage_options' ) ) {
|
490 |
+
return;
|
491 |
+
}
|
492 |
+
|
493 |
+
if ( get_user_meta( get_current_user_id(), 'roc_dismissed_pro_release_notice', true ) == '1' ) {
|
494 |
+
return;
|
495 |
+
}
|
496 |
+
|
497 |
+
printf(
|
498 |
+
'<div class="notice notice-info is-dismissible" data-dismissible="pro_release_notice"><p><strong>%s</strong> %s</p></div>',
|
499 |
+
__( 'Redis Cache Pro is out!', 'redis-cache' ),
|
500 |
+
sprintf(
|
501 |
+
__( 'A <u>business class</u> object cache backend. Truly reliable, highly-optimized and fully customizable, with a <u>dedicated engineer</u> when you most need it. <a href="%1$s">Learn more »</a>', 'redis-cache' ),
|
502 |
+
network_admin_url( $this->page )
|
503 |
+
)
|
504 |
+
);
|
505 |
+
}
|
506 |
+
|
507 |
+
public function wc_pro_notice() {
|
508 |
+
if ( ! class_exists( 'WooCommerce' ) ) {
|
509 |
+
return;
|
510 |
+
}
|
511 |
+
|
512 |
+
$screen = get_current_screen();
|
513 |
+
|
514 |
+
if ( ! isset( $screen->id ) ) {
|
515 |
+
return;
|
516 |
+
}
|
517 |
+
|
518 |
+
if ( ! in_array( $screen->id, array( 'edit-shop_order', 'edit-product', 'woocommerce_page_wc-admin' ) ) ) {
|
519 |
+
return;
|
520 |
+
}
|
521 |
+
|
522 |
+
if ( ! current_user_can( 'manage_options' ) ) {
|
523 |
+
return;
|
524 |
+
}
|
525 |
+
|
526 |
+
if ( get_user_meta( get_current_user_id(), 'roc_dismissed_wc_pro_notice', true ) == '1' ) {
|
527 |
+
return;
|
528 |
+
}
|
529 |
+
|
530 |
+
printf(
|
531 |
+
'<div class="notice woocommerce-message woocommerce-admin-promo-messages is-dismissible" data-dismissible="wc_pro_notice"><p><strong>%s</strong></p><p>%s</p></div>',
|
532 |
+
__( 'Redis Cache Pro + WooCommerce = ❤️', 'redis-cache' ),
|
533 |
+
sprintf(
|
534 |
+
__( 'Redis Cache Pro is a <u>business class</u> object cache that’s highly-optimized for WooCommerce to provide true reliability, peace of mind and faster load times for your store. <a style="color: #bb77ae;" href="%1$s">Learn more »</a>', 'redis-cache' ),
|
535 |
+
network_admin_url( $this->page )
|
536 |
+
)
|
537 |
+
);
|
538 |
+
}
|
539 |
+
|
540 |
+
public function register_shutdown_hooks() {
|
541 |
+
if ( ! defined( 'WP_REDIS_DISABLE_COMMENT' ) || ! WP_REDIS_DISABLE_COMMENT ) {
|
542 |
+
add_action( 'shutdown', array( $this, 'maybe_print_comment' ), 0 );
|
543 |
+
}
|
544 |
+
}
|
545 |
+
|
546 |
+
public function record_metrics() {
|
547 |
+
global $wp_object_cache;
|
548 |
+
|
549 |
+
if ( defined( 'WP_REDIS_DISABLE_METRICS' ) && WP_REDIS_DISABLE_METRICS ) {
|
550 |
+
return;
|
551 |
+
}
|
552 |
+
|
553 |
+
if ( ! method_exists( $wp_object_cache, 'info' ) || ! method_exists( $wp_object_cache, 'redis_instance' ) ) {
|
554 |
+
return;
|
555 |
+
}
|
556 |
+
|
557 |
+
$info = $wp_object_cache->info();
|
558 |
+
|
559 |
+
$metrics = [
|
560 |
+
'i' => substr( uniqid(), -7 ),
|
561 |
+
'h' => $info->hits,
|
562 |
+
'm' => $info->misses,
|
563 |
+
'r' => $info->ratio,
|
564 |
+
'b' => $info->bytes,
|
565 |
+
't' => number_format( $info->time, 5 ),
|
566 |
+
'c' => $info->calls,
|
567 |
+
];
|
568 |
+
|
569 |
+
$wp_object_cache->redis_instance()->zadd(
|
570 |
+
$wp_object_cache->build_key( 'metrics', 'redis-cache' ),
|
571 |
+
time(),
|
572 |
+
http_build_query( $metrics, null, ';' )
|
573 |
+
);
|
574 |
+
}
|
575 |
+
|
576 |
+
public function discard_metrics() {
|
577 |
+
global $wp_object_cache;
|
578 |
+
|
579 |
+
if ( defined( 'WP_REDIS_DISABLE_METRICS' ) && WP_REDIS_DISABLE_METRICS ) {
|
580 |
+
return;
|
581 |
+
}
|
582 |
+
|
583 |
+
if ( ! method_exists( $wp_object_cache, 'redis_instance' ) ) {
|
584 |
+
return;
|
585 |
+
}
|
586 |
+
|
587 |
+
$wp_object_cache->redis_instance()->zremrangebyscore(
|
588 |
+
$wp_object_cache->build_key( 'metrics', 'redis-cache' ),
|
589 |
+
0,
|
590 |
+
time() - HOUR_IN_SECONDS
|
591 |
+
);
|
592 |
+
}
|
593 |
+
|
594 |
+
public function maybe_print_comment() {
|
595 |
+
global $wp_object_cache;
|
596 |
+
|
597 |
+
if (
|
598 |
+
( defined( 'DOING_CRON' ) && DOING_CRON ) ||
|
599 |
+
( defined( 'DOING_AJAX' ) && DOING_AJAX ) ||
|
600 |
+
( defined( 'REST_REQUEST' ) && REST_REQUEST ) ||
|
601 |
+
( defined( 'JSON_REQUEST' ) && JSON_REQUEST ) ||
|
602 |
+
( defined( 'IFRAME_REQUEST' ) && IFRAME_REQUEST ) ||
|
603 |
+
( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST ) ||
|
604 |
+
( defined( 'WC_API_REQUEST' ) && WC_API_REQUEST )
|
605 |
+
) {
|
606 |
+
return;
|
607 |
+
}
|
608 |
+
|
609 |
+
if ( function_exists( 'wp_is_json_request' ) && wp_is_json_request() ) {
|
610 |
+
return;
|
611 |
+
}
|
612 |
+
|
613 |
+
if (
|
614 |
+
! isset( $wp_object_cache->cache_hits ) ||
|
615 |
+
! isset( $wp_object_cache->diagnostics ) ||
|
616 |
+
! is_array( $wp_object_cache->cache )
|
617 |
+
) {
|
618 |
+
return;
|
619 |
+
}
|
620 |
+
|
621 |
+
$message = sprintf(
|
622 |
+
__( 'Performance optimized by Redis Object Cache. Learn more: %s', 'redis-cache' ),
|
623 |
+
'https://wprediscache.com'
|
624 |
+
);
|
625 |
+
|
626 |
+
if ( ! WP_DEBUG ) {
|
627 |
+
printf( "\n<!-- %s -->\n", $message );
|
628 |
+
|
629 |
+
return;
|
630 |
+
}
|
631 |
+
|
632 |
+
$bytes = strlen( serialize( $wp_object_cache->cache ) );
|
633 |
+
|
634 |
+
$debug = sprintf(
|
635 |
+
__( 'Retrieved %1$d objects (%2$s) from Redis using %3$s.', 'redis-cache' ),
|
636 |
+
$wp_object_cache->cache_hits,
|
637 |
+
function_exists( 'size_format' ) ? size_format( $bytes ) : "{$bytes} bytes",
|
638 |
+
$wp_object_cache->diagnostics['client']
|
639 |
+
);
|
640 |
+
|
641 |
+
printf( "<!--\n%s\n\n%s\n-->\n", $message, $debug );
|
642 |
+
}
|
643 |
+
|
644 |
+
public function initialize_filesystem( $url, $silent = false ) {
|
645 |
+
if ( $silent ) {
|
646 |
+
ob_start();
|
647 |
+
}
|
648 |
+
|
649 |
+
if ( ( $credentials = request_filesystem_credentials( $url ) ) === false ) {
|
650 |
+
if ( $silent ) {
|
651 |
+
ob_end_clean();
|
652 |
+
}
|
653 |
+
|
654 |
+
return false;
|
655 |
+
}
|
656 |
+
|
657 |
+
if ( ! WP_Filesystem( $credentials ) ) {
|
658 |
+
request_filesystem_credentials( $url );
|
659 |
+
|
660 |
+
if ( $silent ) {
|
661 |
+
ob_end_clean();
|
662 |
+
}
|
663 |
+
|
664 |
+
return false;
|
665 |
+
}
|
666 |
+
|
667 |
+
return true;
|
668 |
+
}
|
669 |
+
|
670 |
+
public function maybe_update_dropin( $upgrader, $options ) {
|
671 |
+
global $wp_filesystem;
|
672 |
+
|
673 |
+
if (
|
674 |
+
$options['action'] !== 'update' ||
|
675 |
+
$options['type'] !== 'plugin' ||
|
676 |
+
! is_array( $options['plugins'] ) ||
|
677 |
+
! in_array( WP_REDIS_BASENAME, $options['plugins'] )
|
678 |
+
) {
|
679 |
+
return;
|
680 |
+
}
|
681 |
+
|
682 |
+
if ( ! $this->validate_object_cache_dropin() ) {
|
683 |
+
return;
|
684 |
+
}
|
685 |
+
|
686 |
+
if ( WP_Filesystem() ) {
|
687 |
+
$wp_filesystem->copy(
|
688 |
+
WP_REDIS_PLUGIN_PATH . '/includes/object-cache.php',
|
689 |
+
WP_CONTENT_DIR . '/object-cache.php',
|
690 |
+
true
|
691 |
+
);
|
692 |
+
}
|
693 |
+
}
|
694 |
+
|
695 |
+
public function on_deactivation( $plugin ) {
|
696 |
+
global $wp_filesystem;
|
697 |
+
|
698 |
+
ob_start();
|
699 |
+
|
700 |
+
if ( $plugin === WP_REDIS_BASENAME ) {
|
701 |
+
if ( $timestamp = wp_next_scheduled( 'rediscache_discard_metrics' ) ) {
|
702 |
+
wp_unschedule_event( $timestamp, 'rediscache_discard_metrics' );
|
703 |
+
}
|
704 |
+
|
705 |
+
wp_cache_flush();
|
706 |
+
|
707 |
+
if ( $this->validate_object_cache_dropin() && $this->initialize_filesystem( '', true ) ) {
|
708 |
+
$wp_filesystem->delete( WP_CONTENT_DIR . '/object-cache.php' );
|
709 |
+
}
|
710 |
+
}
|
711 |
+
|
712 |
+
ob_end_clean();
|
713 |
+
}
|
714 |
+
|
715 |
+
public function action_link( $action ) {
|
716 |
+
return wp_nonce_url(
|
717 |
+
network_admin_url( add_query_arg( 'action', $action, $this->page ) ),
|
718 |
+
$action
|
719 |
+
);
|
720 |
+
}
|
721 |
+
}
|
includes/class-qm-collector.php
ADDED
@@ -0,0 +1,93 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Query Monitor data collector
|
4 |
+
*
|
5 |
+
* @package Rhubarb\RedisCache
|
6 |
+
*/
|
7 |
+
|
8 |
+
namespace Rhubarb\RedisCache;
|
9 |
+
|
10 |
+
use QM_Collector as Base_Collector;
|
11 |
+
|
12 |
+
defined( '\\ABSPATH' ) || exit;
|
13 |
+
|
14 |
+
class QM_Collector extends Base_Collector {
|
15 |
+
|
16 |
+
public $id = 'cache';
|
17 |
+
|
18 |
+
public function process() {
|
19 |
+
global $wp_object_cache;
|
20 |
+
|
21 |
+
$this->process_defaults();
|
22 |
+
|
23 |
+
$plugin = Plugin::instance();
|
24 |
+
|
25 |
+
$this->data['status'] = $plugin->get_status();
|
26 |
+
$this->data['has_dropin'] = $plugin->object_cache_dropin_exists();
|
27 |
+
$this->data['valid_dropin'] = $plugin->validate_object_cache_dropin();
|
28 |
+
|
29 |
+
if ( ! method_exists( $wp_object_cache, 'info' ) ) {
|
30 |
+
return;
|
31 |
+
}
|
32 |
+
|
33 |
+
$info = $wp_object_cache->info();
|
34 |
+
|
35 |
+
$this->data['hits'] = $info->hits;
|
36 |
+
$this->data['misses'] = $info->misses;
|
37 |
+
$this->data['ratio'] = $info->ratio;
|
38 |
+
$this->data['bytes'] = $info->bytes;
|
39 |
+
|
40 |
+
$this->data['errors'] = $info->errors;
|
41 |
+
$this->data['meta'] = $info->meta;
|
42 |
+
|
43 |
+
if ( defined( 'WP_REDIS_DISABLED' ) && WP_REDIS_DISABLED ) {
|
44 |
+
$this->data['meta'][ __( 'Disabled', 'redis-cache' ) ] = __( 'Yes', 'redis-cache' );
|
45 |
+
}
|
46 |
+
|
47 |
+
$this->data['groups'] = [
|
48 |
+
'global' => $info->groups->global,
|
49 |
+
'non_persistent' => $info->groups->non_persistent,
|
50 |
+
'unflushable' => $info->groups->unflushable,
|
51 |
+
];
|
52 |
+
|
53 |
+
// these are used by Query Monitor
|
54 |
+
$this->data['stats']['cache_hits'] = $info->hits;
|
55 |
+
$this->data['stats']['cache_misses'] = $info->misses;
|
56 |
+
$this->data['cache_hit_percentage'] = $info->ratio;
|
57 |
+
}
|
58 |
+
|
59 |
+
public function process_defaults() {
|
60 |
+
$this->data['hits'] = 0;
|
61 |
+
$this->data['misses'] = 0;
|
62 |
+
$this->data['ratio'] = 0;
|
63 |
+
$this->data['bytes'] = 0;
|
64 |
+
|
65 |
+
$this->data['cache_hit_percentage'] = 0;
|
66 |
+
|
67 |
+
$this->data['object_cache_extensions'] = [];
|
68 |
+
$this->data['opcode_cache_extensions'] = [];
|
69 |
+
|
70 |
+
if ( function_exists( 'extension_loaded' ) ) {
|
71 |
+
$this->data['object_cache_extensions'] = array_map(
|
72 |
+
'extension_loaded',
|
73 |
+
[
|
74 |
+
'APCu' => 'APCu',
|
75 |
+
'Memcache' => 'Memcache',
|
76 |
+
'Memcached' => 'Memcached',
|
77 |
+
'Redis' => 'Redis',
|
78 |
+
]
|
79 |
+
);
|
80 |
+
|
81 |
+
$this->data['opcode_cache_extensions'] = array_map(
|
82 |
+
'extension_loaded',
|
83 |
+
[
|
84 |
+
'APC' => 'APC',
|
85 |
+
'Zend OPcache' => 'Zend OPcache',
|
86 |
+
]
|
87 |
+
);
|
88 |
+
}
|
89 |
+
|
90 |
+
$this->data['has_object_cache'] = (bool) wp_using_ext_object_cache();
|
91 |
+
$this->data['has_opcode_cache'] = array_filter( $this->data['opcode_cache_extensions'] ) ? true : false;
|
92 |
+
}
|
93 |
+
}
|
includes/class-qm-output.php
ADDED
@@ -0,0 +1,88 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Query Monitor output logic class
|
4 |
+
*
|
5 |
+
* @package Rhubarb\RedisCache
|
6 |
+
*/
|
7 |
+
|
8 |
+
namespace Rhubarb\RedisCache;
|
9 |
+
|
10 |
+
use QM_Output_Html;
|
11 |
+
|
12 |
+
defined( '\\ABSPATH' ) || exit;
|
13 |
+
|
14 |
+
class QM_Output extends QM_Output_Html {
|
15 |
+
|
16 |
+
public function __construct( QM_Collector $collector ) {
|
17 |
+
parent::__construct( $collector );
|
18 |
+
|
19 |
+
add_filter( 'qm/output/menus', [ $this, 'admin_menu' ], 30 );
|
20 |
+
add_filter( 'qm/output/panel_menus', [ $this, 'panel_menu' ] );
|
21 |
+
}
|
22 |
+
|
23 |
+
public function name() {
|
24 |
+
return __( 'Object Cache', 'redis cache' );
|
25 |
+
}
|
26 |
+
|
27 |
+
public function admin_menu( array $menu ) {
|
28 |
+
$data = $this->collector->get_data();
|
29 |
+
|
30 |
+
$title = $data['ratio']
|
31 |
+
? sprintf( '%s (%s%%)', $this->name(), $data['ratio'] )
|
32 |
+
: $this->name();
|
33 |
+
|
34 |
+
$args = [
|
35 |
+
'title' => esc_html( $title ),
|
36 |
+
];
|
37 |
+
|
38 |
+
if ( empty( $data['status'] ) ) {
|
39 |
+
$args['meta']['classname'] = 'qm-alert';
|
40 |
+
}
|
41 |
+
|
42 |
+
if ( ! empty( $data['errors'] ) ) {
|
43 |
+
$args['meta']['classname'] = 'qm-warning';
|
44 |
+
}
|
45 |
+
|
46 |
+
$menu[ $this->collector->id() ] = $this->menu( $args );
|
47 |
+
|
48 |
+
return $menu;
|
49 |
+
}
|
50 |
+
|
51 |
+
public function panel_menu( array $menu ) {
|
52 |
+
$ids = array_keys( $menu );
|
53 |
+
$request = array_search( 'qm-request', $ids, true );
|
54 |
+
$position = false === $request ? count( $menu ) : $request;
|
55 |
+
|
56 |
+
$item = [
|
57 |
+
$this->collector->id() => $this->menu( [ 'title' => $this->name() ] ),
|
58 |
+
];
|
59 |
+
|
60 |
+
return array_merge(
|
61 |
+
array_slice( $menu, 0, $position ),
|
62 |
+
$item,
|
63 |
+
array_slice( $menu, $position )
|
64 |
+
);
|
65 |
+
}
|
66 |
+
|
67 |
+
public function output() {
|
68 |
+
$data = $this->collector->get_data();
|
69 |
+
|
70 |
+
if ( ! $data['has_dropin'] ) {
|
71 |
+
$this->before_non_tabular_output();
|
72 |
+
echo $this->build_notice( __( 'The Redis Object Cache drop-in is not installed. Use WP CLI or go to "Settings -> Redis" to enable drop-in.', 'redis-cache' ) );
|
73 |
+
$this->after_non_tabular_output();
|
74 |
+
|
75 |
+
return;
|
76 |
+
}
|
77 |
+
|
78 |
+
if ( ! $data['valid_dropin'] ) {
|
79 |
+
$this->before_non_tabular_output();
|
80 |
+
echo $this->build_notice( __( 'WordPress is using a foreign object cache drop-in and Redis Object Cache is not being used. Use WP CLI or go to "Settings -> Redis" to enable drop-in.', 'redis-cache' ) );
|
81 |
+
$this->after_non_tabular_output();
|
82 |
+
|
83 |
+
return;
|
84 |
+
}
|
85 |
+
|
86 |
+
require_once WP_REDIS_PLUGIN_PATH . '/includes/ui/query-monitor.php';
|
87 |
+
}
|
88 |
+
}
|
includes/class-ui.php
ADDED
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* UI utility class
|
4 |
+
*
|
5 |
+
* @package Rhubarb\RedisCache
|
6 |
+
*/
|
7 |
+
|
8 |
+
namespace Rhubarb\RedisCache;
|
9 |
+
|
10 |
+
defined( '\\ABSPATH' ) || exit;
|
11 |
+
|
12 |
+
/**
|
13 |
+
* UI class definition
|
14 |
+
*/
|
15 |
+
class UI {
|
16 |
+
|
17 |
+
/**
|
18 |
+
* Holds all registered tabs
|
19 |
+
*
|
20 |
+
* @var array
|
21 |
+
*/
|
22 |
+
private static $tabs = [];
|
23 |
+
|
24 |
+
/**
|
25 |
+
* Registers a settings tab
|
26 |
+
*
|
27 |
+
* @param string $slug Unique slug to identify the tab.
|
28 |
+
* @param string $label Tab label.
|
29 |
+
* @param array $args Optional arguments describing the tab.
|
30 |
+
* @return void
|
31 |
+
*/
|
32 |
+
public static function register_tab( $slug, $label, $args = [] ) {
|
33 |
+
self::$tabs[ $slug ] = (object) wp_parse_args(
|
34 |
+
$args,
|
35 |
+
[
|
36 |
+
'label' => $label,
|
37 |
+
'file' => WP_REDIS_PLUGIN_PATH . "/includes/ui/tabs/{$slug}.php",
|
38 |
+
'slug' => $slug,
|
39 |
+
'target' => "#{$slug}",
|
40 |
+
'default' => false,
|
41 |
+
]
|
42 |
+
);
|
43 |
+
}
|
44 |
+
|
45 |
+
/**
|
46 |
+
* Retrieves all registered tabs
|
47 |
+
*
|
48 |
+
* @return array
|
49 |
+
*/
|
50 |
+
public static function get_tabs() {
|
51 |
+
return self::$tabs;
|
52 |
+
}
|
53 |
+
|
54 |
+
}
|
includes/{wp-cli-commands.php → cli/class-commands.php}
RENAMED
@@ -1,13 +1,27 @@
|
|
1 |
<?php
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2 |
|
3 |
-
WP_CLI
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4 |
|
5 |
/**
|
6 |
* Enables, disabled, updates, and checks the status of the Redis object cache.
|
7 |
*
|
8 |
* @package wp-cli
|
9 |
*/
|
10 |
-
class
|
11 |
|
12 |
/**
|
13 |
* Show the Redis object cache status and (when possible) client.
|
@@ -15,15 +29,13 @@ class RedisObjectCache_CLI_Commands extends WP_CLI_Command {
|
|
15 |
* ## EXAMPLES
|
16 |
*
|
17 |
* wp redis status
|
18 |
-
*
|
19 |
*/
|
20 |
public function status() {
|
21 |
|
22 |
-
$plugin =
|
23 |
$status = $plugin->get_status();
|
24 |
$client = $plugin->get_redis_client_name();
|
25 |
|
26 |
-
|
27 |
switch ( $status ) {
|
28 |
case __( 'Disabled', 'redis-cache' ):
|
29 |
$status = WP_CLI::colorize( "%y{$status}%n" );
|
@@ -53,32 +65,29 @@ class RedisObjectCache_CLI_Commands extends WP_CLI_Command {
|
|
53 |
* ## EXAMPLES
|
54 |
*
|
55 |
* wp redis enable
|
56 |
-
*
|
57 |
*/
|
58 |
public function enable() {
|
59 |
|
60 |
global $wp_filesystem;
|
61 |
|
62 |
-
$plugin =
|
63 |
|
64 |
if ( $plugin->object_cache_dropin_exists() ) {
|
65 |
|
66 |
if ( $plugin->validate_object_cache_dropin() ) {
|
67 |
WP_CLI::line( __( 'Redis object cache already enabled.', 'redis-cache' ) );
|
68 |
} else {
|
69 |
-
WP_CLI::error( __('
|
70 |
}
|
71 |
-
|
72 |
} else {
|
73 |
|
74 |
WP_Filesystem();
|
75 |
|
76 |
-
if ( $wp_filesystem->copy(
|
77 |
WP_CLI::success( __( 'Object cache enabled.', 'redis-cache' ) );
|
78 |
} else {
|
79 |
WP_CLI::error( __( 'Object cache could not be enabled.', 'redis-cache' ) );
|
80 |
}
|
81 |
-
|
82 |
}
|
83 |
|
84 |
}
|
@@ -92,13 +101,12 @@ class RedisObjectCache_CLI_Commands extends WP_CLI_Command {
|
|
92 |
* ## EXAMPLES
|
93 |
*
|
94 |
* wp redis disable
|
95 |
-
*
|
96 |
*/
|
97 |
public function disable() {
|
98 |
|
99 |
global $wp_filesystem;
|
100 |
|
101 |
-
$plugin =
|
102 |
|
103 |
if ( ! $plugin->object_cache_dropin_exists() ) {
|
104 |
|
@@ -108,7 +116,7 @@ class RedisObjectCache_CLI_Commands extends WP_CLI_Command {
|
|
108 |
|
109 |
if ( ! $plugin->validate_object_cache_dropin() ) {
|
110 |
|
111 |
-
WP_CLI::error( __( '
|
112 |
|
113 |
} else {
|
114 |
|
@@ -119,9 +127,7 @@ class RedisObjectCache_CLI_Commands extends WP_CLI_Command {
|
|
119 |
} else {
|
120 |
WP_CLI::error( __( 'Object cache could not be disabled.', 'redis-cache' ) );
|
121 |
}
|
122 |
-
|
123 |
}
|
124 |
-
|
125 |
}
|
126 |
|
127 |
}
|
@@ -143,7 +149,7 @@ class RedisObjectCache_CLI_Commands extends WP_CLI_Command {
|
|
143 |
|
144 |
WP_Filesystem();
|
145 |
|
146 |
-
if ( $wp_filesystem->copy(
|
147 |
WP_CLI::success( __( 'Updated object cache drop-in and enabled Redis object cache.', 'redis-cache' ) );
|
148 |
} else {
|
149 |
WP_CLI::error( __( 'Object cache drop-in could not be updated.', 'redis-cache' ) );
|
1 |
<?php
|
2 |
+
/**
|
3 |
+
* WP CLI command class
|
4 |
+
*
|
5 |
+
* @package Rhubarb\RedisCache
|
6 |
+
*/
|
7 |
+
|
8 |
+
namespace Rhubarb\RedisCache\CLI;
|
9 |
|
10 |
+
use WP_CLI;
|
11 |
+
use WP_CLI_Command;
|
12 |
+
|
13 |
+
use WP_Filesystem;
|
14 |
+
|
15 |
+
use Rhubarb\RedisCache\Plugin;
|
16 |
+
|
17 |
+
defined( '\\ABSPATH' ) || exit;
|
18 |
|
19 |
/**
|
20 |
* Enables, disabled, updates, and checks the status of the Redis object cache.
|
21 |
*
|
22 |
* @package wp-cli
|
23 |
*/
|
24 |
+
class Commands extends WP_CLI_Command {
|
25 |
|
26 |
/**
|
27 |
* Show the Redis object cache status and (when possible) client.
|
29 |
* ## EXAMPLES
|
30 |
*
|
31 |
* wp redis status
|
|
|
32 |
*/
|
33 |
public function status() {
|
34 |
|
35 |
+
$plugin = Plugin::instance();
|
36 |
$status = $plugin->get_status();
|
37 |
$client = $plugin->get_redis_client_name();
|
38 |
|
|
|
39 |
switch ( $status ) {
|
40 |
case __( 'Disabled', 'redis-cache' ):
|
41 |
$status = WP_CLI::colorize( "%y{$status}%n" );
|
65 |
* ## EXAMPLES
|
66 |
*
|
67 |
* wp redis enable
|
|
|
68 |
*/
|
69 |
public function enable() {
|
70 |
|
71 |
global $wp_filesystem;
|
72 |
|
73 |
+
$plugin = Plugin::instance();
|
74 |
|
75 |
if ( $plugin->object_cache_dropin_exists() ) {
|
76 |
|
77 |
if ( $plugin->validate_object_cache_dropin() ) {
|
78 |
WP_CLI::line( __( 'Redis object cache already enabled.', 'redis-cache' ) );
|
79 |
} else {
|
80 |
+
WP_CLI::error( __( 'A foreign object cache drop-in was found. To use Redis for object caching, run: `wp redis update-dropin`.', 'redis-cache' ) );
|
81 |
}
|
|
|
82 |
} else {
|
83 |
|
84 |
WP_Filesystem();
|
85 |
|
86 |
+
if ( $wp_filesystem->copy( WP_REDIS_PLUGIN_PATH . '/includes/object-cache.php', WP_CONTENT_DIR . '/object-cache.php', true ) ) {
|
87 |
WP_CLI::success( __( 'Object cache enabled.', 'redis-cache' ) );
|
88 |
} else {
|
89 |
WP_CLI::error( __( 'Object cache could not be enabled.', 'redis-cache' ) );
|
90 |
}
|
|
|
91 |
}
|
92 |
|
93 |
}
|
101 |
* ## EXAMPLES
|
102 |
*
|
103 |
* wp redis disable
|
|
|
104 |
*/
|
105 |
public function disable() {
|
106 |
|
107 |
global $wp_filesystem;
|
108 |
|
109 |
+
$plugin = Plugin::instance();
|
110 |
|
111 |
if ( ! $plugin->object_cache_dropin_exists() ) {
|
112 |
|
116 |
|
117 |
if ( ! $plugin->validate_object_cache_dropin() ) {
|
118 |
|
119 |
+
WP_CLI::error( __( 'A foreign object cache drop-in was found. To use Redis for object caching, run: `wp redis update-dropin`.', 'redis-cache' ) );
|
120 |
|
121 |
} else {
|
122 |
|
127 |
} else {
|
128 |
WP_CLI::error( __( 'Object cache could not be disabled.', 'redis-cache' ) );
|
129 |
}
|
|
|
130 |
}
|
|
|
131 |
}
|
132 |
|
133 |
}
|
149 |
|
150 |
WP_Filesystem();
|
151 |
|
152 |
+
if ( $wp_filesystem->copy( WP_REDIS_PLUGIN_PATH . '/includes/object-cache.php', WP_CONTENT_DIR . '/object-cache.php', true ) ) {
|
153 |
WP_CLI::success( __( 'Updated object cache drop-in and enabled Redis object cache.', 'redis-cache' ) );
|
154 |
} else {
|
155 |
WP_CLI::error( __( 'Object cache drop-in could not be updated.', 'redis-cache' ) );
|
includes/diagnostics.php
DELETED
@@ -1,110 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
global $wp_object_cache;
|
4 |
-
|
5 |
-
$info = $plugins = $dropins = array();
|
6 |
-
$dropin = $this->validate_object_cache_dropin() && ( ! defined('WP_REDIS_DISABLED') || ! WP_REDIS_DISABLED );
|
7 |
-
|
8 |
-
$info[ 'Status' ] = $this->get_status();
|
9 |
-
$info[ 'Client' ] = $this->get_redis_client_name();
|
10 |
-
$info[ 'Drop-in' ] = $dropin ? 'Valid' : 'Invalid';
|
11 |
-
|
12 |
-
if ( $dropin ) {
|
13 |
-
try {
|
14 |
-
$cache = new WP_Object_Cache( false );
|
15 |
-
$info[ 'Ping' ] = defined( 'WP_REDIS_CLUSTER' ) ? 'Not supported' : $cache->redis_instance()->ping();
|
16 |
-
} catch ( Exception $exception ) {
|
17 |
-
$info[ 'Connection Exception' ] = sprintf( '%s (%s)', $exception->getMessage(), get_class( $exception ) );
|
18 |
-
}
|
19 |
-
}
|
20 |
-
|
21 |
-
$info[ 'Redis Extension' ] = class_exists( 'Redis' ) ? phpversion( 'redis' ) : 'Not Found';
|
22 |
-
$info[ 'Predis Client' ] = class_exists( 'Predis\Client' ) ? Predis\Client::VERSION : 'Not Found';
|
23 |
-
|
24 |
-
if ( defined( 'PHP_VERSION' ) ) {
|
25 |
-
$info[ 'PHP Version' ] = PHP_VERSION;
|
26 |
-
}
|
27 |
-
|
28 |
-
if ( defined( 'HHVM_VERSION' ) ) {
|
29 |
-
$info[ 'HHVM Version' ] = HHVM_VERSION;
|
30 |
-
}
|
31 |
-
|
32 |
-
$info[ 'Redis Version' ] = $this->get_redis_version() ?: 'Unknown';
|
33 |
-
|
34 |
-
$info[ 'Multisite' ] = is_multisite() ? 'Yes' : 'No';
|
35 |
-
|
36 |
-
if ( $dropin ) {
|
37 |
-
$info[ 'Global Prefix' ] = json_encode( $wp_object_cache->global_prefix );
|
38 |
-
$info[ 'Blog Prefix' ] = json_encode( $wp_object_cache->blog_prefix );
|
39 |
-
}
|
40 |
-
|
41 |
-
$constants = array(
|
42 |
-
'WP_REDIS_DISABLED',
|
43 |
-
'WP_REDIS_CLIENT',
|
44 |
-
'WP_REDIS_SCHEME',
|
45 |
-
'WP_REDIS_PATH',
|
46 |
-
'WP_REDIS_HOST',
|
47 |
-
'WP_REDIS_PORT',
|
48 |
-
'WP_REDIS_DATABASE',
|
49 |
-
'WP_REDIS_TIMEOUT',
|
50 |
-
'WP_REDIS_READ_TIMEOUT',
|
51 |
-
'WP_REDIS_RETRY_INTERVAL',
|
52 |
-
'WP_REDIS_SERVERS',
|
53 |
-
'WP_REDIS_CLUSTER',
|
54 |
-
'WP_REDIS_SHARDS',
|
55 |
-
'WP_REDIS_SENTINEL',
|
56 |
-
'WP_REDIS_IGBINARY',
|
57 |
-
'WP_REDIS_MAXTTL',
|
58 |
-
'WP_CACHE_KEY_SALT',
|
59 |
-
'WP_REDIS_GLOBAL_GROUPS',
|
60 |
-
'WP_REDIS_IGNORED_GROUPS',
|
61 |
-
'WP_REDIS_UNFLUSHABLE_GROUPS',
|
62 |
-
);
|
63 |
-
|
64 |
-
foreach ( $constants as $constant ) {
|
65 |
-
if ( defined( $constant ) ) {
|
66 |
-
$info[ $constant ] = json_encode( constant( $constant ) );
|
67 |
-
}
|
68 |
-
}
|
69 |
-
|
70 |
-
if ( defined( 'WP_REDIS_PASSWORD' ) ) {
|
71 |
-
$info[ 'WP_REDIS_PASSWORD' ] = json_encode( empty( WP_REDIS_PASSWORD ) ? null : str_repeat( '*', strlen( WP_REDIS_PASSWORD ) ) );
|
72 |
-
}
|
73 |
-
|
74 |
-
if ( $dropin ) {
|
75 |
-
$info[ 'Global Groups' ] = json_encode( $wp_object_cache->global_groups, JSON_PRETTY_PRINT );
|
76 |
-
$info[ 'Ignored Groups' ] = json_encode( $wp_object_cache->ignored_groups, JSON_PRETTY_PRINT );
|
77 |
-
$info[ 'Unflushable Groups' ] = json_encode( $wp_object_cache->unflushable_groups, JSON_PRETTY_PRINT );
|
78 |
-
}
|
79 |
-
|
80 |
-
foreach ( $info as $name => $value ) {
|
81 |
-
echo "{$name}: {$value}\r\n";
|
82 |
-
}
|
83 |
-
|
84 |
-
foreach ( get_dropins() as $file => $details ) {
|
85 |
-
$dropins[ $file ] = sprintf(
|
86 |
-
' - %s v%s by %s',
|
87 |
-
$details[ 'Name' ],
|
88 |
-
$details[ 'Version' ],
|
89 |
-
$details[ 'Author' ]
|
90 |
-
);
|
91 |
-
}
|
92 |
-
|
93 |
-
if ( ! empty( $dropins ) ) {
|
94 |
-
echo "Dropins: \r\n", implode( "\r\n", $dropins ), "\r\n";
|
95 |
-
}
|
96 |
-
|
97 |
-
foreach ( get_plugins() as $file => $details ) {
|
98 |
-
$plugins[] = sprintf(
|
99 |
-
' - %s v%s by %s (%s%s)',
|
100 |
-
$details[ 'Name' ],
|
101 |
-
$details[ 'Version' ],
|
102 |
-
$details[ 'Author' ],
|
103 |
-
is_plugin_active( $file ) ? 'Active' : 'Inactive',
|
104 |
-
is_multisite() ? ( is_plugin_active_for_network( $file ) ? ' network-wide' : '' ) : ''
|
105 |
-
);
|
106 |
-
}
|
107 |
-
|
108 |
-
if ( ! empty( $plugins ) ) {
|
109 |
-
echo "Plugins: \r\n", implode( "\r\n", $plugins ), "\r\n";
|
110 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
includes/object-cache.php
CHANGED
@@ -1,19 +1,22 @@
|
|
1 |
<?php
|
2 |
-
|
3 |
-
Plugin Name: Redis Object Cache Drop-In
|
4 |
-
Plugin URI: http://wordpress.org/plugins/redis-cache/
|
5 |
-
Description: A persistent object cache backend powered by Redis. Supports Predis, PhpRedis, HHVM, replication, clustering and WP-CLI.
|
6 |
-
Version:
|
7 |
-
Author: Till Krüss
|
8 |
-
Author URI: https://
|
9 |
-
License: GPLv3
|
10 |
-
License URI: http://www.gnu.org/licenses/gpl-3.0.html
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
|
|
|
|
|
|
17 |
|
18 |
/**
|
19 |
* Adds a value to cache.
|
@@ -26,15 +29,12 @@ if (! defined('WP_REDIS_DISABLED') || ! WP_REDIS_DISABLED) :
|
|
26 |
* @param string $group The group value appended to the $key.
|
27 |
* @param int $expiration The expiration time, defaults to 0.
|
28 |
*
|
29 |
-
* @global WP_Object_Cache $wp_object_cache
|
30 |
-
*
|
31 |
* @return bool Returns TRUE on success or FALSE on failure.
|
32 |
*/
|
33 |
-
function wp_cache_add($key, $value, $group = '', $expiration = 0)
|
34 |
-
{
|
35 |
global $wp_object_cache;
|
36 |
|
37 |
-
return $wp_object_cache->add($key, $value, $group, $expiration);
|
38 |
}
|
39 |
|
40 |
/**
|
@@ -47,9 +47,8 @@ function wp_cache_add($key, $value, $group = '', $expiration = 0)
|
|
47 |
*
|
48 |
* @return bool Always returns True
|
49 |
*/
|
50 |
-
function wp_cache_close()
|
51 |
-
|
52 |
-
return true;
|
53 |
}
|
54 |
|
55 |
/**
|
@@ -59,15 +58,12 @@ function wp_cache_close()
|
|
59 |
* @param int $offset The amount by which to decrement the item's value.
|
60 |
* @param string $group The group value appended to the $key.
|
61 |
*
|
62 |
-
* @global WP_Object_Cache $wp_object_cache
|
63 |
-
*
|
64 |
* @return int|bool Returns item's new value on success or FALSE on failure.
|
65 |
*/
|
66 |
-
function wp_cache_decr($key, $offset = 1, $group = '')
|
67 |
-
{
|
68 |
global $wp_object_cache;
|
69 |
|
70 |
-
return $wp_object_cache->decrement($key, $offset, $group);
|
71 |
}
|
72 |
|
73 |
/**
|
@@ -77,32 +73,26 @@ function wp_cache_decr($key, $offset = 1, $group = '')
|
|
77 |
* @param string $group The group value appended to the $key.
|
78 |
* @param int $time The amount of time the server will wait to delete the item in seconds.
|
79 |
*
|
80 |
-
* @
|
81 |
-
*
|
82 |
-
* @return bool Returns TRUE on success or FALSE on failure.
|
83 |
*/
|
84 |
-
function wp_cache_delete($key, $group = '', $time = 0)
|
85 |
-
{
|
86 |
global $wp_object_cache;
|
87 |
|
88 |
-
return $wp_object_cache->delete($key, $group, $time);
|
89 |
}
|
90 |
|
91 |
/**
|
92 |
* Invalidate all items in the cache. If `WP_REDIS_SELECTIVE_FLUSH` is `true`,
|
93 |
-
* only keys prefixed with the `
|
94 |
*
|
95 |
* @param int $delay Number of seconds to wait before invalidating the items.
|
96 |
*
|
97 |
-
* @
|
98 |
-
*
|
99 |
-
* @return bool Returns TRUE on success or FALSE on failure.
|
100 |
*/
|
101 |
-
function wp_cache_flush($delay = 0)
|
102 |
-
{
|
103 |
global $wp_object_cache;
|
104 |
|
105 |
-
return $wp_object_cache->flush($delay);
|
106 |
}
|
107 |
|
108 |
/**
|
@@ -110,44 +100,34 @@ function wp_cache_flush($delay = 0)
|
|
110 |
*
|
111 |
* Gets an object from cache based on $key and $group.
|
112 |
*
|
113 |
-
* @param string
|
114 |
-
* @param string
|
115 |
-
* @param bool
|
116 |
-
*
|
117 |
-
* @param bool
|
118 |
-
*
|
119 |
-
*
|
120 |
-
* @global WP_Object_Cache $wp_object_cache
|
121 |
*
|
122 |
-
* @return bool|mixed
|
123 |
*/
|
124 |
-
function wp_cache_get($key, $group = '', $force = false, &$found = null)
|
125 |
-
{
|
126 |
global $wp_object_cache;
|
127 |
|
128 |
-
return $wp_object_cache->get($key, $group, $force, $found);
|
129 |
}
|
130 |
|
131 |
/**
|
132 |
-
*
|
133 |
-
*
|
134 |
-
* Gets multiple values from cache, including across multiple groups
|
135 |
-
*
|
136 |
-
* Usage: array( 'group0' => array( 'key0', 'key1', 'key2', ), 'group1' => array( 'key0' ) )
|
137 |
-
*
|
138 |
-
* Mirrors the Memcached Object Cache plugin's argument and return-value formats
|
139 |
-
*
|
140 |
-
* @param array $groups Array of groups and keys to retrieve
|
141 |
-
*
|
142 |
-
* @global WP_Object_Cache $wp_object_cache
|
143 |
*
|
144 |
-
* @
|
|
|
|
|
|
|
|
|
145 |
*/
|
146 |
-
function
|
147 |
-
{
|
148 |
global $wp_object_cache;
|
149 |
|
150 |
-
return $wp_object_cache->
|
151 |
}
|
152 |
|
153 |
/**
|
@@ -157,32 +137,31 @@ function wp_cache_get_multi($groups)
|
|
157 |
* @param int $offset The amount by which to increment the item's value.
|
158 |
* @param string $group The group value appended to the $key.
|
159 |
*
|
160 |
-
* @global WP_Object_Cache $wp_object_cache
|
161 |
-
*
|
162 |
* @return int|bool Returns item's new value on success or FALSE on failure.
|
163 |
*/
|
164 |
-
function wp_cache_incr($key, $offset = 1, $group = '')
|
165 |
-
{
|
166 |
global $wp_object_cache;
|
167 |
|
168 |
-
return $wp_object_cache->increment($key, $offset, $group);
|
169 |
}
|
170 |
|
171 |
/**
|
172 |
* Sets up Object Cache Global and assigns it.
|
173 |
*
|
174 |
-
* @global WP_Object_Cache $wp_object_cache WordPress Object Cache
|
175 |
-
*
|
176 |
* @return void
|
177 |
*/
|
178 |
-
function wp_cache_init()
|
179 |
-
{
|
180 |
global $wp_object_cache;
|
181 |
|
182 |
-
|
183 |
-
|
|
|
|
|
184 |
|
185 |
-
|
|
|
|
|
|
|
186 |
}
|
187 |
}
|
188 |
|
@@ -197,15 +176,12 @@ function wp_cache_init()
|
|
197 |
* @param string $group The group value appended to the $key.
|
198 |
* @param int $expiration The expiration time, defaults to 0.
|
199 |
*
|
200 |
-
* @global WP_Object_Cache $wp_object_cache
|
201 |
-
*
|
202 |
* @return bool Returns TRUE on success or FALSE on failure.
|
203 |
*/
|
204 |
-
function wp_cache_replace($key, $value, $group = '', $expiration = 0)
|
205 |
-
{
|
206 |
global $wp_object_cache;
|
207 |
|
208 |
-
return $wp_object_cache->replace($key, $value, $group, $expiration);
|
209 |
}
|
210 |
|
211 |
/**
|
@@ -218,15 +194,12 @@ function wp_cache_replace($key, $value, $group = '', $expiration = 0)
|
|
218 |
* @param string $group The group value appended to the $key.
|
219 |
* @param int $expiration The expiration time, defaults to 0.
|
220 |
*
|
221 |
-
* @global WP_Object_Cache $wp_object_cache
|
222 |
-
*
|
223 |
* @return bool Returns TRUE on success or FALSE on failure.
|
224 |
*/
|
225 |
-
function wp_cache_set($key, $value, $group = '', $expiration = 0)
|
226 |
-
{
|
227 |
global $wp_object_cache;
|
228 |
|
229 |
-
return $wp_object_cache->set($key, $value, $group, $expiration);
|
230 |
}
|
231 |
|
232 |
/**
|
@@ -236,15 +209,12 @@ function wp_cache_set($key, $value, $group = '', $expiration = 0)
|
|
236 |
*
|
237 |
* @param int $_blog_id Blog ID
|
238 |
*
|
239 |
-
* @global WP_Object_Cache $wp_object_cache
|
240 |
-
*
|
241 |
* @return bool
|
242 |
*/
|
243 |
-
function wp_cache_switch_to_blog($_blog_id)
|
244 |
-
{
|
245 |
global $wp_object_cache;
|
246 |
|
247 |
-
return $wp_object_cache->switch_to_blog($_blog_id);
|
248 |
}
|
249 |
|
250 |
/**
|
@@ -252,15 +222,12 @@ function wp_cache_switch_to_blog($_blog_id)
|
|
252 |
*
|
253 |
* @param string|array $groups A group or an array of groups to add.
|
254 |
*
|
255 |
-
* @global WP_Object_Cache $wp_object_cache
|
256 |
-
*
|
257 |
* @return void
|
258 |
*/
|
259 |
-
function wp_cache_add_global_groups($groups)
|
260 |
-
{
|
261 |
global $wp_object_cache;
|
262 |
|
263 |
-
$wp_object_cache->add_global_groups($groups);
|
264 |
}
|
265 |
|
266 |
/**
|
@@ -268,19 +235,16 @@ function wp_cache_add_global_groups($groups)
|
|
268 |
*
|
269 |
* @param string|array $groups A group or an array of groups to add.
|
270 |
*
|
271 |
-
* @global WP_Object_Cache $wp_object_cache
|
272 |
-
*
|
273 |
* @return void
|
274 |
*/
|
275 |
-
function wp_cache_add_non_persistent_groups($groups)
|
276 |
-
{
|
277 |
global $wp_object_cache;
|
278 |
|
279 |
-
$wp_object_cache->add_non_persistent_groups($groups);
|
280 |
}
|
281 |
|
282 |
-
class WP_Object_Cache
|
283 |
-
|
284 |
/**
|
285 |
* The Redis client.
|
286 |
*
|
@@ -296,7 +260,7 @@ class WP_Object_Cache
|
|
296 |
private $redis_version = null;
|
297 |
|
298 |
/**
|
299 |
-
* Track if Redis is available
|
300 |
*
|
301 |
* @var bool
|
302 |
*/
|
@@ -317,11 +281,18 @@ class WP_Object_Cache
|
|
317 |
public $cache = array();
|
318 |
|
319 |
/**
|
320 |
-
*
|
321 |
*
|
322 |
-
* @var
|
323 |
*/
|
324 |
-
public $
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
325 |
|
326 |
/**
|
327 |
* List of global groups.
|
@@ -360,7 +331,11 @@ class WP_Object_Cache
|
|
360 |
*
|
361 |
* @var array
|
362 |
*/
|
363 |
-
public $ignored_groups = array(
|
|
|
|
|
|
|
|
|
364 |
|
365 |
/**
|
366 |
* Prefix used for global groups.
|
@@ -377,243 +352,507 @@ class WP_Object_Cache
|
|
377 |
public $blog_prefix = '';
|
378 |
|
379 |
/**
|
380 |
-
* Track how many requests were found in cache
|
381 |
*
|
382 |
* @var int
|
383 |
*/
|
384 |
public $cache_hits = 0;
|
385 |
|
386 |
/**
|
387 |
-
* Track how may requests were not cached
|
388 |
*
|
389 |
* @var int
|
390 |
*/
|
391 |
public $cache_misses = 0;
|
392 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
393 |
/**
|
394 |
* Instantiate the Redis class.
|
395 |
*
|
396 |
* @param bool $fail_gracefully
|
397 |
*/
|
398 |
-
public function __construct($fail_gracefully = true)
|
399 |
-
{
|
400 |
global $blog_id, $table_prefix;
|
401 |
|
402 |
$this->fail_gracefully = $fail_gracefully;
|
403 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
404 |
$parameters = array(
|
405 |
'scheme' => 'tcp',
|
406 |
'host' => '127.0.0.1',
|
407 |
'port' => 6379,
|
408 |
-
'
|
409 |
-
'
|
410 |
-
'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
411 |
);
|
412 |
|
413 |
-
foreach (
|
414 |
-
$constant = sprintf('WP_REDIS_%s', strtoupper($setting));
|
415 |
|
416 |
-
if (defined($constant)) {
|
417 |
-
$parameters[$setting] = constant($constant);
|
418 |
}
|
419 |
}
|
420 |
|
421 |
-
if (
|
422 |
-
$
|
423 |
-
}
|
424 |
|
425 |
-
|
426 |
-
|
|
|
427 |
}
|
428 |
|
429 |
-
|
430 |
-
|
431 |
-
}
|
432 |
|
433 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
434 |
|
435 |
-
|
436 |
-
$client = defined('HHVM_VERSION') ? 'hhvm' : 'pecl';
|
437 |
-
} else {
|
438 |
-
$client = 'predis';
|
439 |
-
}
|
440 |
|
441 |
-
|
442 |
-
|
443 |
-
$this->redis_client = sprintf('HHVM Extension (v%s)', HHVM_VERSION);
|
444 |
-
$this->redis = new Redis();
|
445 |
-
|
446 |
-
// Adjust host and port, if the scheme is `unix`
|
447 |
-
if (strcasecmp('unix', $parameters['scheme']) === 0) {
|
448 |
-
$parameters['host'] = 'unix://' . $parameters['path'];
|
449 |
-
$parameters['port'] = 0;
|
450 |
-
}
|
451 |
|
452 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
453 |
|
454 |
-
|
455 |
-
|
456 |
-
}
|
457 |
}
|
458 |
|
459 |
-
|
460 |
-
|
461 |
-
|
462 |
-
|
463 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
464 |
);
|
|
|
465 |
|
466 |
-
|
467 |
-
|
468 |
-
|
469 |
-
|
470 |
-
null,
|
471 |
-
array_values(WP_REDIS_CLUSTER),
|
472 |
-
$parameters['timeout'],
|
473 |
-
$parameters['read_timeout'],
|
474 |
-
];
|
475 |
-
|
476 |
-
if (isset($parameters['password']) && version_compare($phpredis_version, '4.3.0', '>=')) {
|
477 |
-
$connection_args[] = $parameters['password'];
|
478 |
-
}
|
479 |
|
480 |
-
|
481 |
-
|
482 |
-
|
483 |
-
|
484 |
-
$connection_args = [
|
485 |
-
$parameters['host'],
|
486 |
-
$parameters['port'],
|
487 |
-
$parameters['timeout'],
|
488 |
-
null,
|
489 |
-
$parameters['retry_interval'],
|
490 |
-
];
|
491 |
-
|
492 |
-
if (strcasecmp('tls', $parameters['scheme']) === 0) {
|
493 |
-
$connection_args[0] = sprintf(
|
494 |
-
'%s://%s',
|
495 |
-
$parameters['scheme'],
|
496 |
-
str_replace('tls://', '', $parameters['host'])
|
497 |
-
);
|
498 |
-
}
|
499 |
|
500 |
-
|
501 |
-
$connection_args[0] = $parameters['path'];
|
502 |
-
$connection_args[1] = null;
|
503 |
-
}
|
504 |
|
505 |
-
|
506 |
-
|
507 |
-
|
|
|
508 |
|
509 |
-
|
510 |
-
|
511 |
-
|
512 |
-
);
|
513 |
}
|
514 |
|
515 |
-
|
516 |
-
|
|
|
|
|
517 |
}
|
518 |
}
|
519 |
|
520 |
-
|
521 |
-
|
522 |
-
$this->redis->auth($parameters['password']);
|
523 |
-
}
|
524 |
|
525 |
-
|
526 |
-
|
527 |
-
|
528 |
-
|
529 |
|
530 |
-
|
531 |
-
|
532 |
-
|
|
|
|
|
|
|
|
|
|
|
533 |
|
534 |
-
|
535 |
-
|
|
|
|
|
536 |
|
537 |
-
|
538 |
-
|
539 |
-
|
540 |
-
|
|
|
|
|
541 |
|
542 |
-
|
543 |
-
|
544 |
-
|
545 |
-
|
546 |
-
|
547 |
-
|
548 |
|
549 |
-
|
550 |
-
|
551 |
-
|
552 |
-
|
553 |
-
|
554 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
555 |
|
556 |
-
|
557 |
-
|
558 |
-
if (
|
559 |
-
$parameters =
|
560 |
-
} elseif (defined('WP_REDIS_SENTINEL')) {
|
561 |
-
$parameters = WP_REDIS_SERVERS;
|
562 |
-
$options['replication'] = 'sentinel';
|
563 |
-
$options['service'] = WP_REDIS_SENTINEL;
|
564 |
-
} elseif (defined('WP_REDIS_SERVERS')) {
|
565 |
-
$parameters = WP_REDIS_SERVERS;
|
566 |
-
$options['replication'] = true;
|
567 |
-
} elseif (defined('WP_REDIS_CLUSTER')) {
|
568 |
-
$parameters = WP_REDIS_CLUSTER;
|
569 |
-
$options['cluster'] = 'redis';
|
570 |
-
}
|
571 |
-
|
572 |
-
if (isset($parameters['read_timeout']) && $parameters['read_timeout']) {
|
573 |
-
$parameters['read_write_timeout'] = $parameters['read_timeout'];
|
574 |
}
|
575 |
|
576 |
-
|
577 |
-
|
578 |
-
$options['parameters']['password'] = WP_REDIS_PASSWORD;
|
579 |
-
}
|
580 |
}
|
|
|
|
|
|
|
|
|
|
|
581 |
|
582 |
-
|
583 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
584 |
|
585 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
586 |
}
|
|
|
587 |
|
588 |
-
|
589 |
-
|
|
|
|
|
|
|
590 |
} else {
|
591 |
-
|
|
|
|
|
592 |
}
|
|
|
593 |
|
594 |
-
|
595 |
-
|
596 |
-
|
597 |
-
|
598 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
599 |
}
|
600 |
|
601 |
-
|
602 |
-
|
603 |
-
|
604 |
-
$
|
605 |
}
|
|
|
|
|
606 |
}
|
607 |
|
608 |
-
$this->
|
609 |
-
|
610 |
-
$
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
611 |
}
|
612 |
|
613 |
-
//
|
614 |
-
|
615 |
-
|
616 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
617 |
}
|
618 |
}
|
619 |
|
@@ -622,9 +861,8 @@ class WP_Object_Cache
|
|
622 |
*
|
623 |
* @return bool
|
624 |
*/
|
625 |
-
public function redis_status()
|
626 |
-
|
627 |
-
return $this->redis_connected;
|
628 |
}
|
629 |
|
630 |
/**
|
@@ -632,9 +870,8 @@ class WP_Object_Cache
|
|
632 |
*
|
633 |
* @return mixed
|
634 |
*/
|
635 |
-
public function redis_instance()
|
636 |
-
|
637 |
-
return $this->redis;
|
638 |
}
|
639 |
|
640 |
/**
|
@@ -642,9 +879,8 @@ class WP_Object_Cache
|
|
642 |
*
|
643 |
* @return null|string
|
644 |
*/
|
645 |
-
public function redis_version()
|
646 |
-
|
647 |
-
return $this->redis_version;
|
648 |
}
|
649 |
|
650 |
/**
|
@@ -659,9 +895,8 @@ class WP_Object_Cache
|
|
659 |
* @param int $expiration The expiration time, defaults to 0.
|
660 |
* @return bool Returns TRUE on success or FALSE on failure.
|
661 |
*/
|
662 |
-
public function add($key, $value, $group = 'default', $expiration = 0)
|
663 |
-
|
664 |
-
return $this->add_or_replace(true, $key, $value, $group, $expiration);
|
665 |
}
|
666 |
|
667 |
/**
|
@@ -676,9 +911,8 @@ class WP_Object_Cache
|
|
676 |
* @param int $expiration The expiration time, defaults to 0.
|
677 |
* @return bool Returns TRUE on success or FALSE on failure.
|
678 |
*/
|
679 |
-
public function replace($key, $value, $group = 'default', $expiration = 0)
|
680 |
-
|
681 |
-
return $this->add_or_replace(false, $key, $value, $group, $expiration);
|
682 |
}
|
683 |
|
684 |
/**
|
@@ -693,50 +927,77 @@ class WP_Object_Cache
|
|
693 |
* @param int $expiration The expiration time, defaults to 0.
|
694 |
* @return bool Returns TRUE on success or FALSE on failure.
|
695 |
*/
|
696 |
-
protected function add_or_replace($add, $key, $value, $group = 'default', $expiration = 0)
|
697 |
-
|
698 |
-
$cache_addition_suspended = function_exists('wp_suspend_cache_addition')
|
699 |
? wp_suspend_cache_addition()
|
700 |
: false;
|
701 |
|
702 |
-
if ($add && $cache_addition_suspended) {
|
703 |
return false;
|
704 |
}
|
705 |
|
706 |
$result = true;
|
707 |
-
$derived_key = $this->build_key($key, $group);
|
708 |
|
709 |
// save if group not excluded and redis is up
|
710 |
-
if (! $this->is_ignored_group($group) && $this->redis_status()) {
|
711 |
try {
|
712 |
-
$
|
|
|
713 |
|
714 |
-
if ($add
|
715 |
-
|
716 |
-
}
|
717 |
|
718 |
-
|
|
|
719 |
|
720 |
-
|
721 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
722 |
} else {
|
723 |
-
$result = $this->parse_redis_response($this->redis->set($derived_key, $this->maybe_serialize($value)));
|
724 |
}
|
725 |
-
|
726 |
-
$
|
|
|
|
|
|
|
|
|
|
|
727 |
|
728 |
return false;
|
729 |
}
|
730 |
}
|
731 |
|
732 |
-
$exists = isset($this->cache[$derived_key]);
|
733 |
|
734 |
-
if ($add == $exists) {
|
735 |
return false;
|
736 |
}
|
737 |
|
738 |
-
if ($result) {
|
739 |
-
$this->add_to_internal_cache($derived_key, $value);
|
740 |
}
|
741 |
|
742 |
return $result;
|
@@ -749,31 +1010,34 @@ class WP_Object_Cache
|
|
749 |
* @param string $group The group value appended to the $key.
|
750 |
* @return bool Returns TRUE on success or FALSE on failure.
|
751 |
*/
|
752 |
-
public function delete($key, $group = 'default')
|
753 |
-
{
|
754 |
-
$start_time = microtime(true);
|
755 |
-
|
756 |
$result = false;
|
757 |
-
$derived_key = $this->build_key($key, $group);
|
758 |
|
759 |
-
if (isset($this->cache[$derived_key])) {
|
760 |
-
unset($this->cache[$derived_key]);
|
761 |
$result = true;
|
762 |
}
|
763 |
|
764 |
-
|
|
|
|
|
765 |
try {
|
766 |
-
$result = $this->parse_redis_response($this->redis->del($derived_key));
|
767 |
-
} catch (Exception $exception) {
|
768 |
-
$this->handle_exception($exception);
|
769 |
|
770 |
return false;
|
771 |
}
|
772 |
}
|
773 |
|
774 |
-
|
775 |
-
|
776 |
-
|
|
|
|
|
|
|
|
|
777 |
}
|
778 |
|
779 |
return $result;
|
@@ -781,88 +1045,87 @@ class WP_Object_Cache
|
|
781 |
|
782 |
/**
|
783 |
* Invalidate all items in the cache. If `WP_REDIS_SELECTIVE_FLUSH` is `true`,
|
784 |
-
* only keys prefixed with the `
|
785 |
*
|
786 |
* @param int $delay Number of seconds to wait before invalidating the items.
|
787 |
* @return bool Returns TRUE on success or FALSE on failure.
|
788 |
*/
|
789 |
-
public function flush($delay = 0)
|
790 |
-
|
791 |
-
$delay = abs(intval($delay));
|
792 |
|
793 |
-
if ($delay) {
|
794 |
-
sleep($delay);
|
795 |
}
|
796 |
|
797 |
$results = [];
|
798 |
$this->cache = array();
|
799 |
|
800 |
-
if ($this->redis_status()) {
|
801 |
-
$salt = defined('
|
802 |
-
$selective = defined('WP_REDIS_SELECTIVE_FLUSH') ? WP_REDIS_SELECTIVE_FLUSH : null;
|
803 |
|
804 |
-
$start_time = microtime(true);
|
805 |
|
806 |
-
if ($salt && $selective) {
|
807 |
-
$script = $this->get_flush_closure($salt);
|
808 |
|
809 |
-
if (defined('WP_REDIS_CLUSTER')) {
|
810 |
try {
|
811 |
-
foreach ($this->redis->_masters() as $master) {
|
812 |
-
$redis = new Redis;
|
813 |
-
$redis->connect($master[0], $master[1]);
|
814 |
-
$results[] = $this->parse_redis_response($script());
|
815 |
-
unset($redis);
|
816 |
}
|
817 |
-
} catch (Exception $exception) {
|
818 |
-
$this->handle_exception($exception);
|
819 |
|
820 |
return false;
|
821 |
}
|
822 |
} else {
|
823 |
try {
|
824 |
-
$results[] = $this->parse_redis_response($script());
|
825 |
-
} catch (Exception $exception) {
|
826 |
-
$this->handle_exception($exception);
|
827 |
|
828 |
return false;
|
829 |
}
|
830 |
}
|
831 |
} else {
|
832 |
-
if (defined('WP_REDIS_CLUSTER')) {
|
833 |
try {
|
834 |
-
foreach ($this->redis->_masters() as $master) {
|
835 |
-
$results[] = $this->parse_redis_response($this->redis->flushdb($master));
|
836 |
}
|
837 |
-
} catch (Exception $exception) {
|
838 |
-
$this->handle_exception($exception);
|
839 |
|
840 |
return false;
|
841 |
}
|
842 |
} else {
|
843 |
try {
|
844 |
-
$results[] = $this->parse_redis_response($this->redis->flushdb());
|
845 |
-
} catch (Exception $exception) {
|
846 |
-
$this->handle_exception($exception);
|
847 |
|
848 |
return false;
|
849 |
}
|
850 |
}
|
851 |
}
|
852 |
|
853 |
-
if (function_exists('do_action')) {
|
854 |
-
$execute_time = microtime(true) - $start_time;
|
855 |
|
856 |
-
do_action('redis_object_cache_flush', $results, $delay, $selective, $salt, $execute_time);
|
857 |
}
|
858 |
}
|
859 |
|
860 |
-
if (empty($results)) {
|
861 |
return false;
|
862 |
}
|
863 |
|
864 |
-
foreach ($results as $result) {
|
865 |
-
if (! $result) {
|
866 |
return false;
|
867 |
}
|
868 |
}
|
@@ -873,26 +1136,28 @@ class WP_Object_Cache
|
|
873 |
/**
|
874 |
* Returns a closure to flush selectively.
|
875 |
*
|
876 |
-
* @param string
|
877 |
* @return callable Generated callable executing the lua script.
|
878 |
*/
|
879 |
-
protected function get_flush_closure($salt)
|
880 |
-
|
881 |
-
|
882 |
-
return $this->lua_flush_extended_closure($salt);
|
883 |
} else {
|
884 |
-
return $this->lua_flush_closure($salt);
|
885 |
}
|
886 |
}
|
887 |
|
888 |
-
protected function glob_quote($string) {
|
889 |
-
$characters = ['*', '+', '?', '!', '{', '}', '[', ']', '(', ')', '|', '@'];
|
890 |
|
891 |
return str_replace(
|
892 |
$characters,
|
893 |
-
array_map(
|
894 |
-
|
895 |
-
|
|
|
|
|
|
|
896 |
$string
|
897 |
);
|
898 |
}
|
@@ -900,14 +1165,13 @@ class WP_Object_Cache
|
|
900 |
/**
|
901 |
* Returns a closure ready to be called to flush selectively ignoring unflushable groups.
|
902 |
*
|
903 |
-
* @param string
|
904 |
* @return callable Generated callable executing the lua script.
|
905 |
*/
|
906 |
-
protected function lua_flush_closure($salt)
|
907 |
-
|
908 |
-
$salt = $this->glob_quote($salt);
|
909 |
|
910 |
-
return function () use ($salt) {
|
911 |
$script = <<<LUA
|
912 |
local cur = 0
|
913 |
local i = 0
|
@@ -925,34 +1189,36 @@ class WP_Object_Cache
|
|
925 |
return i
|
926 |
LUA;
|
927 |
|
928 |
-
if (version_compare($this->redis_version(), '5', '<') && version_compare($this->redis_version(), '3.2', '>=')) {
|
929 |
$script = 'redis.replicate_commands()' . "\n" . $script;
|
930 |
}
|
931 |
|
932 |
-
$args = ($this->redis instanceof Predis\Client)
|
933 |
-
? [$script, 0]
|
934 |
-
: [$script];
|
935 |
|
936 |
-
return call_user_func_array([$this->redis, 'eval'], $args);
|
937 |
};
|
938 |
}
|
939 |
|
940 |
/**
|
941 |
* Returns a closure ready to be called to flush selectively.
|
942 |
*
|
943 |
-
* @param string
|
944 |
* @return callable Generated callable executing the lua script.
|
945 |
*/
|
946 |
-
protected function lua_flush_extended_closure($salt)
|
947 |
-
|
948 |
-
$salt = $this->glob_quote($salt);
|
949 |
|
950 |
-
return function () use ($salt) {
|
951 |
-
$salt_length = strlen($salt);
|
952 |
|
953 |
-
$unflushable = array_map(
|
954 |
-
|
955 |
-
|
|
|
|
|
|
|
956 |
|
957 |
$script = <<<LUA
|
958 |
local cur = 0
|
@@ -977,15 +1243,15 @@ LUA;
|
|
977 |
until 0 == cur
|
978 |
return i
|
979 |
LUA;
|
980 |
-
if (version_compare($this->redis_version(), '5', '<') && version_compare($this->redis_version(), '3.2', '>=')) {
|
981 |
$script = 'redis.replicate_commands()' . "\n" . $script;
|
982 |
}
|
983 |
|
984 |
-
$args = ($this->redis instanceof Predis\Client)
|
985 |
-
? array_merge([$script, count($unflushable)], $unflushable)
|
986 |
-
: [$script, $unflushable, count($unflushable)];
|
987 |
|
988 |
-
return call_user_func_array([$this->redis, 'eval'], $args);
|
989 |
};
|
990 |
}
|
991 |
|
@@ -994,41 +1260,45 @@ LUA;
|
|
994 |
*
|
995 |
* Gets an object from cache based on $key and $group.
|
996 |
*
|
997 |
-
* @param string
|
998 |
-
* @param string
|
999 |
-
* @param string
|
1000 |
-
*
|
1001 |
-
* @param bool
|
1002 |
-
*
|
1003 |
-
* @return bool|mixed
|
1004 |
*/
|
1005 |
-
public function get($key, $group = 'default', $force = false, &$found = null)
|
1006 |
-
|
1007 |
-
$start_time = microtime(true);
|
1008 |
|
1009 |
-
|
1010 |
-
|
1011 |
-
if (isset($this->cache[$derived_key]) && ! $force) {
|
1012 |
$found = true;
|
1013 |
$this->cache_hits++;
|
1014 |
|
1015 |
-
return $this->get_from_internal_cache($derived_key
|
1016 |
-
} elseif ($this->is_ignored_group($group) || ! $this->redis_status()) {
|
1017 |
$found = false;
|
1018 |
$this->cache_misses++;
|
1019 |
|
1020 |
return false;
|
1021 |
}
|
1022 |
|
|
|
|
|
1023 |
try {
|
1024 |
-
$result = $this->redis->get($derived_key);
|
1025 |
-
} catch (Exception $exception) {
|
1026 |
-
$this->handle_exception($exception);
|
1027 |
|
1028 |
return false;
|
1029 |
}
|
1030 |
|
1031 |
-
|
|
|
|
|
|
|
|
|
|
|
1032 |
$found = false;
|
1033 |
$this->cache_misses++;
|
1034 |
|
@@ -1036,20 +1306,18 @@ LUA;
|
|
1036 |
} else {
|
1037 |
$found = true;
|
1038 |
$this->cache_hits++;
|
1039 |
-
$value = $this->maybe_unserialize($result);
|
1040 |
}
|
1041 |
|
1042 |
-
$this->add_to_internal_cache($derived_key, $value);
|
1043 |
-
|
1044 |
-
if (function_exists('do_action')) {
|
1045 |
-
$execute_time = microtime(true) - $start_time;
|
1046 |
|
1047 |
-
|
|
|
1048 |
}
|
1049 |
|
1050 |
-
if (function_exists('apply_filters') && function_exists('has_filter')) {
|
1051 |
-
if (has_filter('redis_object_cache_get_value')) {
|
1052 |
-
return apply_filters('redis_object_cache_get_value', $value, $key, $group, $force, $found);
|
1053 |
}
|
1054 |
}
|
1055 |
|
@@ -1057,67 +1325,90 @@ LUA;
|
|
1057 |
}
|
1058 |
|
1059 |
/**
|
1060 |
-
*
|
1061 |
-
*
|
1062 |
-
* Gets multiple values from cache, including across multiple groups
|
1063 |
-
*
|
1064 |
-
* Usage: array( 'group0' => array( 'key0', 'key1', 'key2', ), 'group1' => array( 'key0' ) )
|
1065 |
*
|
1066 |
-
*
|
1067 |
-
*
|
1068 |
-
* @param
|
1069 |
-
*
|
|
|
1070 |
*/
|
1071 |
-
public function
|
1072 |
-
|
1073 |
-
if (empty($groups) || ! is_array($groups)) {
|
1074 |
return false;
|
1075 |
}
|
1076 |
|
1077 |
-
// Retrieve requested caches and reformat results to mimic Memcached Object Cache's output
|
1078 |
$cache = array();
|
|
|
1079 |
|
1080 |
-
foreach ($
|
1081 |
-
|
1082 |
-
|
1083 |
-
$cache[$this->build_key($key, $group)] = $this->get($key, $group);
|
1084 |
-
}
|
1085 |
-
} else {
|
1086 |
-
// Reformat arguments as expected by Redis
|
1087 |
-
$derived_keys = array();
|
1088 |
|
1089 |
-
|
1090 |
-
|
1091 |
-
|
|
|
|
|
1092 |
|
1093 |
-
|
1094 |
-
|
1095 |
-
$group_cache = $this->redis->mget($derived_keys);
|
1096 |
-
} catch (Exception $exception) {
|
1097 |
-
$this->handle_exception($exception);
|
1098 |
-
$group_cache = array_fill(0, count($derived_keys) - 1, false);
|
1099 |
-
}
|
1100 |
|
1101 |
-
|
1102 |
-
$group_cache = array_combine($derived_keys, $group_cache);
|
1103 |
|
1104 |
-
|
1105 |
-
|
|
|
|
|
1106 |
|
1107 |
-
|
1108 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1109 |
|
1110 |
-
|
1111 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
1112 |
}
|
1113 |
|
1114 |
-
|
1115 |
-
|
1116 |
-
|
1117 |
-
|
1118 |
-
|
1119 |
-
|
|
|
1120 |
$this->cache_misses++;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1121 |
}
|
1122 |
}
|
1123 |
|
@@ -1135,39 +1426,41 @@ LUA;
|
|
1135 |
* @param int $expiration The expiration time, defaults to 0.
|
1136 |
* @return bool Returns TRUE on success or FALSE on failure.
|
1137 |
*/
|
1138 |
-
public function set($key, $value, $group = 'default', $expiration = 0)
|
1139 |
-
{
|
1140 |
-
$start_time = microtime(true);
|
1141 |
-
|
1142 |
$result = true;
|
1143 |
-
$derived_key = $this->build_key($key, $group);
|
|
|
|
|
1144 |
|
1145 |
// save if group not excluded from redis and redis is up
|
1146 |
-
if (! $this->is_ignored_group($group) && $this->redis_status()) {
|
1147 |
-
$expiration = apply_filters('redis_cache_expiration', $this->validate_expiration($expiration), $key, $group);
|
1148 |
|
1149 |
try {
|
1150 |
-
if ($expiration) {
|
1151 |
-
$result = $this->parse_redis_response($this->redis->setex($derived_key, $expiration, $this->maybe_serialize($value)));
|
1152 |
} else {
|
1153 |
-
$result = $this->parse_redis_response($this->redis->set($derived_key, $this->maybe_serialize($value)));
|
1154 |
}
|
1155 |
-
} catch (Exception $exception) {
|
1156 |
-
$this->handle_exception($exception);
|
1157 |
|
1158 |
return false;
|
1159 |
}
|
|
|
|
|
|
|
1160 |
}
|
1161 |
|
1162 |
// if the set was successful, or we didn't go to redis
|
1163 |
-
if ($result) {
|
1164 |
-
$this->add_to_internal_cache($derived_key, $value);
|
1165 |
}
|
1166 |
|
1167 |
-
if (function_exists('do_action')) {
|
1168 |
-
$execute_time = microtime(true) - $start_time;
|
1169 |
|
1170 |
-
do_action('redis_object_cache_set', $key, $value, $group, $expiration, $execute_time);
|
1171 |
}
|
1172 |
|
1173 |
return $result;
|
@@ -1181,31 +1474,36 @@ LUA;
|
|
1181 |
* @param string $group
|
1182 |
* @return int|bool
|
1183 |
*/
|
1184 |
-
public function increment($key, $offset = 1, $group = 'default')
|
1185 |
-
{
|
1186 |
-
$derived_key = $this->build_key($key, $group);
|
1187 |
$offset = (int) $offset;
|
|
|
1188 |
|
1189 |
// If group is a non-Redis group, save to internal cache, not Redis
|
1190 |
-
if ($this->is_ignored_group($group) || ! $this->redis_status()) {
|
1191 |
-
$value = $this->get_from_internal_cache($derived_key
|
1192 |
$value += $offset;
|
1193 |
-
$this->add_to_internal_cache($derived_key, $value);
|
1194 |
|
1195 |
return $value;
|
1196 |
}
|
1197 |
|
1198 |
-
|
|
|
1199 |
try {
|
1200 |
-
$result = $this->parse_redis_response($this->redis->incrBy($derived_key, $offset));
|
1201 |
|
1202 |
-
$this->add_to_internal_cache($derived_key, (int) $this->redis->get($derived_key));
|
1203 |
-
} catch (Exception $exception) {
|
1204 |
-
$this->handle_exception($exception);
|
1205 |
|
1206 |
return false;
|
1207 |
}
|
1208 |
|
|
|
|
|
|
|
|
|
|
|
1209 |
return $result;
|
1210 |
}
|
1211 |
|
@@ -1217,9 +1515,8 @@ LUA;
|
|
1217 |
* @param string $group
|
1218 |
* @return bool
|
1219 |
*/
|
1220 |
-
public function incr($key, $offset = 1, $group = 'default')
|
1221 |
-
|
1222 |
-
return $this->increment($key, $offset, $group);
|
1223 |
}
|
1224 |
|
1225 |
/**
|
@@ -1230,31 +1527,37 @@ LUA;
|
|
1230 |
* @param string $group
|
1231 |
* @return int|bool
|
1232 |
*/
|
1233 |
-
public function decrement($key, $offset = 1, $group = 'default')
|
1234 |
-
|
1235 |
-
$derived_key = $this->build_key($key, $group);
|
1236 |
$offset = (int) $offset;
|
1237 |
|
1238 |
// If group is a non-Redis group, save to internal cache, not Redis
|
1239 |
-
if ($this->is_ignored_group($group) || ! $this->redis_status()) {
|
1240 |
-
$value = $this->get_from_internal_cache($derived_key
|
1241 |
$value -= $offset;
|
1242 |
-
$this->add_to_internal_cache($derived_key, $value);
|
1243 |
|
1244 |
return $value;
|
1245 |
}
|
1246 |
|
|
|
|
|
1247 |
try {
|
1248 |
// Save to Redis
|
1249 |
-
$result = $this->parse_redis_response($this->redis->decrBy($derived_key, $offset));
|
1250 |
|
1251 |
-
$this->add_to_internal_cache($derived_key, (int) $this->redis->get($derived_key));
|
1252 |
-
} catch (Exception $exception) {
|
1253 |
-
$this->handle_exception($exception);
|
1254 |
|
1255 |
return false;
|
1256 |
}
|
1257 |
|
|
|
|
|
|
|
|
|
|
|
1258 |
return $result;
|
1259 |
}
|
1260 |
|
@@ -1263,22 +1566,66 @@ LUA;
|
|
1263 |
*
|
1264 |
* @return string
|
1265 |
*/
|
1266 |
-
public function stats()
|
1267 |
-
|
1268 |
-
|
1269 |
-
|
1270 |
-
|
1271 |
-
|
1272 |
-
|
1273 |
-
|
1274 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1275 |
</p>
|
|
|
|
|
1276 |
|
1277 |
-
|
1278 |
-
|
1279 |
-
|
1280 |
-
|
1281 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1282 |
}
|
1283 |
|
1284 |
/**
|
@@ -1289,19 +1636,18 @@ LUA;
|
|
1289 |
*
|
1290 |
* @return string
|
1291 |
*/
|
1292 |
-
public function build_key($key, $group = 'default')
|
1293 |
-
|
1294 |
-
if (empty($group)) {
|
1295 |
$group = 'default';
|
1296 |
}
|
1297 |
|
1298 |
-
$salt = defined('
|
1299 |
-
$prefix = $this->is_global_group($group) ? $this->global_prefix : $this->blog_prefix;
|
1300 |
|
1301 |
-
$key = $this->sanitize_key_part($key);
|
1302 |
-
$group = $this->sanitize_key_part($group);
|
1303 |
|
1304 |
-
$prefix = trim($prefix, '_-:$');
|
1305 |
|
1306 |
return "{$salt}{$prefix}:{$group}:{$key}";
|
1307 |
}
|
@@ -1309,63 +1655,41 @@ LUA;
|
|
1309 |
/**
|
1310 |
* Replaces the set group separator by another one
|
1311 |
*
|
1312 |
-
* @param string
|
1313 |
-
* @return string
|
1314 |
*/
|
1315 |
-
protected function sanitize_key_part( $part )
|
1316 |
-
|
1317 |
-
return str_replace(':', '-', $part);
|
1318 |
}
|
1319 |
|
1320 |
/**
|
1321 |
* Checks if the given group is part the ignored group array
|
1322 |
*
|
1323 |
-
* @param string
|
1324 |
* @return bool
|
1325 |
*/
|
1326 |
-
protected function is_ignored_group($group)
|
1327 |
-
|
1328 |
-
return in_array($this->sanitize_key_part($group), $this->ignored_groups, true);
|
1329 |
}
|
1330 |
|
1331 |
/**
|
1332 |
* Checks if the given group is part the global group array
|
1333 |
*
|
1334 |
-
* @param string
|
1335 |
* @return bool
|
1336 |
*/
|
1337 |
-
protected function is_global_group($group)
|
1338 |
-
|
1339 |
-
return in_array($this->sanitize_key_part($group), $this->global_groups, true);
|
1340 |
}
|
1341 |
|
1342 |
/**
|
1343 |
* Checks if the given group is part the unflushable group array
|
1344 |
*
|
1345 |
-
* @param string
|
1346 |
* @return bool
|
1347 |
*/
|
1348 |
-
protected function is_unflushable_group($group)
|
1349 |
-
|
1350 |
-
return in_array($this->sanitize_key_part($group), $this->unflushable_groups, true);
|
1351 |
-
}
|
1352 |
-
|
1353 |
-
/**
|
1354 |
-
* Convert data types when using Redis MGET
|
1355 |
-
*
|
1356 |
-
* When requesting multiple keys, those not found in cache are assigned the value null upon return.
|
1357 |
-
* Expected value in this case is false, so we convert
|
1358 |
-
*
|
1359 |
-
* @param string $value Value to possibly convert
|
1360 |
-
* @return string Converted value
|
1361 |
-
*/
|
1362 |
-
protected function filter_redis_get_multi($value)
|
1363 |
-
{
|
1364 |
-
if (is_null($value)) {
|
1365 |
-
$value = false;
|
1366 |
-
}
|
1367 |
-
|
1368 |
-
return $value;
|
1369 |
}
|
1370 |
|
1371 |
/**
|
@@ -1374,17 +1698,16 @@ LUA;
|
|
1374 |
* @param mixed $response
|
1375 |
* @return mixed
|
1376 |
*/
|
1377 |
-
protected function parse_redis_response($response)
|
1378 |
-
|
1379 |
-
if (is_bool($response)) {
|
1380 |
return $response;
|
1381 |
}
|
1382 |
|
1383 |
-
if (is_numeric($response)) {
|
1384 |
return $response;
|
1385 |
}
|
1386 |
|
1387 |
-
if (is_object($response) && method_exists($response, 'getPayload')) {
|
1388 |
return $response->getPayload() === 'OK';
|
1389 |
}
|
1390 |
|
@@ -1397,34 +1720,31 @@ LUA;
|
|
1397 |
* @param string $derived_key Key to save value under.
|
1398 |
* @param mixed $value Object value.
|
1399 |
*/
|
1400 |
-
public function add_to_internal_cache($derived_key, $value)
|
1401 |
-
|
1402 |
-
if (is_object($value)) {
|
1403 |
$value = clone $value;
|
1404 |
}
|
1405 |
|
1406 |
-
$this->cache[$derived_key] = $value;
|
1407 |
}
|
1408 |
|
1409 |
/**
|
1410 |
* Get a value specifically from the internal, run-time cache, not Redis.
|
1411 |
*
|
1412 |
* @param int|string $derived_key Key value.
|
1413 |
-
* @param int|string $group Group that the value belongs to.
|
1414 |
*
|
1415 |
* @return bool|mixed Value on success; false on failure.
|
1416 |
*/
|
1417 |
-
public function get_from_internal_cache($derived_key
|
1418 |
-
|
1419 |
-
if (! isset($this->cache[$derived_key])) {
|
1420 |
return false;
|
1421 |
}
|
1422 |
|
1423 |
-
if (is_object($this->cache[$derived_key])) {
|
1424 |
-
return clone $this->cache[$derived_key];
|
1425 |
}
|
1426 |
|
1427 |
-
return $this->cache[$derived_key];
|
1428 |
}
|
1429 |
|
1430 |
/**
|
@@ -1433,9 +1753,8 @@ LUA;
|
|
1433 |
* @param int $_blog_id
|
1434 |
* @return bool
|
1435 |
*/
|
1436 |
-
public function switch_to_blog($_blog_id)
|
1437 |
-
|
1438 |
-
if (! function_exists('is_multisite') || ! is_multisite()) {
|
1439 |
return false;
|
1440 |
}
|
1441 |
|
@@ -1449,27 +1768,25 @@ LUA;
|
|
1449 |
*
|
1450 |
* @param array $groups List of groups that are global.
|
1451 |
*/
|
1452 |
-
public function add_global_groups($groups)
|
1453 |
-
{
|
1454 |
$groups = (array) $groups;
|
1455 |
|
1456 |
-
if ($this->redis_status()) {
|
1457 |
-
$this->global_groups = array_unique(array_merge($this->global_groups, $groups));
|
1458 |
} else {
|
1459 |
-
$this->ignored_groups = array_unique(array_merge($this->ignored_groups, $groups));
|
1460 |
}
|
1461 |
}
|
1462 |
|
1463 |
/**
|
1464 |
* Sets the list of groups not to be cached by Redis.
|
1465 |
*
|
1466 |
-
* @param array $groups
|
1467 |
*/
|
1468 |
-
public function add_non_persistent_groups($groups)
|
1469 |
-
{
|
1470 |
$groups = (array) $groups;
|
1471 |
|
1472 |
-
$this->ignored_groups = array_unique(array_merge($this->ignored_groups, $groups));
|
1473 |
}
|
1474 |
|
1475 |
/**
|
@@ -1477,26 +1794,24 @@ LUA;
|
|
1477 |
*
|
1478 |
* @param array $groups List of groups that are unflushable.
|
1479 |
*/
|
1480 |
-
public function add_unflushable_groups($groups)
|
1481 |
-
{
|
1482 |
$groups = (array) $groups;
|
1483 |
|
1484 |
-
$this->unflushable_groups = array_unique(array_merge($this->unflushable_groups, $groups));
|
1485 |
}
|
1486 |
|
1487 |
/**
|
1488 |
* Wrapper to validate the cache keys expiration value
|
1489 |
*
|
1490 |
-
* @param mixed $expiration
|
1491 |
*/
|
1492 |
-
protected function validate_expiration($expiration)
|
1493 |
-
|
1494 |
-
$expiration = is_int($expiration) || ctype_digit($expiration) ? (int) $expiration : 0;
|
1495 |
|
1496 |
-
if (defined('WP_REDIS_MAXTTL')) {
|
1497 |
$max = (int) WP_REDIS_MAXTTL;
|
1498 |
|
1499 |
-
if ($expiration === 0 || $expiration > $max) {
|
1500 |
$expiration = $max;
|
1501 |
}
|
1502 |
}
|
@@ -1507,24 +1822,23 @@ LUA;
|
|
1507 |
/**
|
1508 |
* Unserialize value only if it was serialized.
|
1509 |
*
|
1510 |
-
* @param string $original
|
1511 |
-
* @return mixed
|
1512 |
*/
|
1513 |
-
protected function maybe_unserialize($original)
|
1514 |
-
|
1515 |
-
if (defined('WP_REDIS_SERIALIZER') && ! empty(WP_REDIS_SERIALIZER)) {
|
1516 |
return $original;
|
1517 |
}
|
1518 |
|
1519 |
-
if (defined('WP_REDIS_IGBINARY') && WP_REDIS_IGBINARY && function_exists('igbinary_unserialize')) {
|
1520 |
-
return igbinary_unserialize($original);
|
1521 |
}
|
1522 |
|
1523 |
// don't attempt to unserialize data that wasn't serialized going in
|
1524 |
-
if ($this->is_serialized($original)) {
|
1525 |
-
$value = @unserialize($original);
|
1526 |
|
1527 |
-
return is_object($value) ? clone $value : $value;
|
1528 |
}
|
1529 |
|
1530 |
return $original;
|
@@ -1533,29 +1847,28 @@ LUA;
|
|
1533 |
/**
|
1534 |
* Serialize data, if needed.
|
1535 |
*
|
1536 |
-
* @param
|
1537 |
-
* @return mixed
|
1538 |
*/
|
1539 |
-
protected function maybe_serialize($data)
|
1540 |
-
|
1541 |
-
if (is_object($data)) {
|
1542 |
$data = clone $data;
|
1543 |
}
|
1544 |
|
1545 |
-
if (defined('WP_REDIS_SERIALIZER') && ! empty(WP_REDIS_SERIALIZER)) {
|
1546 |
return $data;
|
1547 |
}
|
1548 |
|
1549 |
-
if (defined(
|
1 |
<?php
|
2 |
+
/**
|
3 |
+
* Plugin Name: Redis Object Cache Drop-In
|
4 |
+
* Plugin URI: http://wordpress.org/plugins/redis-cache/
|
5 |
+
* Description: A persistent object cache backend powered by Redis. Supports Predis, PhpRedis, Credis, HHVM, replication, clustering and WP-CLI.
|
6 |
+
* Version: 2.0.0
|
7 |
+
* Author: Till Krüss
|
8 |
+
* Author URI: https://wprediscache.com
|
9 |
+
* License: GPLv3
|
10 |
+
* License URI: http://www.gnu.org/licenses/gpl-3.0.html
|
11 |
+
*
|
12 |
+
* @package Rhubarb\RedisCache
|
13 |
+
*/
|
14 |
+
|
15 |
+
defined( '\\ABSPATH' ) || exit;
|
16 |
+
|
17 |
+
if ( defined( 'WP_REDIS_DISABLED' ) && ! WP_REDIS_DISABLED ) {
|
18 |
+
return;
|
19 |
+
}
|
20 |
|
21 |
/**
|
22 |
* Adds a value to cache.
|
29 |
* @param string $group The group value appended to the $key.
|
30 |
* @param int $expiration The expiration time, defaults to 0.
|
31 |
*
|
|
|
|
|
32 |
* @return bool Returns TRUE on success or FALSE on failure.
|
33 |
*/
|
34 |
+
function wp_cache_add( $key, $value, $group = '', $expiration = 0 ) {
|
|
|
35 |
global $wp_object_cache;
|
36 |
|
37 |
+
return $wp_object_cache->add( $key, $value, $group, $expiration );
|
38 |
}
|
39 |
|
40 |
/**
|
47 |
*
|
48 |
* @return bool Always returns True
|
49 |
*/
|
50 |
+
function wp_cache_close() {
|
51 |
+
return true;
|
|
|
52 |
}
|
53 |
|
54 |
/**
|
58 |
* @param int $offset The amount by which to decrement the item's value.
|
59 |
* @param string $group The group value appended to the $key.
|
60 |
*
|
|
|
|
|
61 |
* @return int|bool Returns item's new value on success or FALSE on failure.
|
62 |
*/
|
63 |
+
function wp_cache_decr( $key, $offset = 1, $group = '' ) {
|
|
|
64 |
global $wp_object_cache;
|
65 |
|
66 |
+
return $wp_object_cache->decrement( $key, $offset, $group );
|
67 |
}
|
68 |
|
69 |
/**
|
73 |
* @param string $group The group value appended to the $key.
|
74 |
* @param int $time The amount of time the server will wait to delete the item in seconds.
|
75 |
*
|
76 |
+
* @return bool Returns TRUE on success or FALSE on failure.
|
|
|
|
|
77 |
*/
|
78 |
+
function wp_cache_delete( $key, $group = '', $time = 0 ) {
|
|
|
79 |
global $wp_object_cache;
|
80 |
|
81 |
+
return $wp_object_cache->delete( $key, $group, $time );
|
82 |
}
|
83 |
|
84 |
/**
|
85 |
* Invalidate all items in the cache. If `WP_REDIS_SELECTIVE_FLUSH` is `true`,
|
86 |
+
* only keys prefixed with the `WP_REDIS_PREFIX` are flushed.
|
87 |
*
|
88 |
* @param int $delay Number of seconds to wait before invalidating the items.
|
89 |
*
|
90 |
+
* @return bool Returns TRUE on success or FALSE on failure.
|
|
|
|
|
91 |
*/
|
92 |
+
function wp_cache_flush( $delay = 0 ) {
|
|
|
93 |
global $wp_object_cache;
|
94 |
|
95 |
+
return $wp_object_cache->flush( $delay );
|
96 |
}
|
97 |
|
98 |
/**
|
100 |
*
|
101 |
* Gets an object from cache based on $key and $group.
|
102 |
*
|
103 |
+
* @param string $key The key under which to store the value.
|
104 |
+
* @param string $group The group value appended to the $key.
|
105 |
+
* @param bool $force Optional. Whether to force an update of the local cache from the persistent
|
106 |
+
* cache. Default false.
|
107 |
+
* @param bool &$found Optional. Whether the key was found in the cache. Disambiguates a return of false,
|
108 |
+
* a storable value. Passed by reference. Default null.
|
|
|
|
|
109 |
*
|
110 |
+
* @return bool|mixed Cached object value.
|
111 |
*/
|
112 |
+
function wp_cache_get( $key, $group = '', $force = false, &$found = null ) {
|
|
|
113 |
global $wp_object_cache;
|
114 |
|
115 |
+
return $wp_object_cache->get( $key, $group, $force, $found );
|
116 |
}
|
117 |
|
118 |
/**
|
119 |
+
* Retrieves multiple values from the cache in one call.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
120 |
*
|
121 |
+
* @param array $keys Array of keys under which the cache contents are stored.
|
122 |
+
* @param string $group Optional. Where the cache contents are grouped. Default empty.
|
123 |
+
* @param bool $force Optional. Whether to force an update of the local cache
|
124 |
+
* from the persistent cache. Default false.
|
125 |
+
* @return array Array of values organized into groups.
|
126 |
*/
|
127 |
+
function wp_cache_get_multiple( $keys, $group = '', $force = false ) {
|
|
|
128 |
global $wp_object_cache;
|
129 |
|
130 |
+
return $wp_object_cache->get_multiple( $keys, $group, $force );
|
131 |
}
|
132 |
|
133 |
/**
|
137 |
* @param int $offset The amount by which to increment the item's value.
|
138 |
* @param string $group The group value appended to the $key.
|
139 |
*
|
|
|
|
|
140 |
* @return int|bool Returns item's new value on success or FALSE on failure.
|
141 |
*/
|
142 |
+
function wp_cache_incr( $key, $offset = 1, $group = '' ) {
|
|
|
143 |
global $wp_object_cache;
|
144 |
|
145 |
+
return $wp_object_cache->increment( $key, $offset, $group );
|
146 |
}
|
147 |
|
148 |
/**
|
149 |
* Sets up Object Cache Global and assigns it.
|
150 |
*
|
|
|
|
|
151 |
* @return void
|
152 |
*/
|
153 |
+
function wp_cache_init() {
|
|
|
154 |
global $wp_object_cache;
|
155 |
|
156 |
+
// backwards compatibility: map `WP_CACHE_KEY_SALT` constant to `WP_REDIS_PREFIX`
|
157 |
+
if ( defined( 'WP_CACHE_KEY_SALT' ) && ! defined( 'WP_REDIS_PREFIX' ) ) {
|
158 |
+
define( 'WP_REDIS_PREFIX', WP_CACHE_KEY_SALT );
|
159 |
+
}
|
160 |
|
161 |
+
if ( ! ( $wp_object_cache instanceof WP_Object_Cache ) ) {
|
162 |
+
$fail_gracefully = ! defined( 'WP_REDIS_GRACEFUL' ) || WP_REDIS_GRACEFUL;
|
163 |
+
|
164 |
+
$wp_object_cache = new WP_Object_Cache( $fail_gracefully );
|
165 |
}
|
166 |
}
|
167 |
|
176 |
* @param string $group The group value appended to the $key.
|
177 |
* @param int $expiration The expiration time, defaults to 0.
|
178 |
*
|
|
|
|
|
179 |
* @return bool Returns TRUE on success or FALSE on failure.
|
180 |
*/
|
181 |
+
function wp_cache_replace( $key, $value, $group = '', $expiration = 0 ) {
|
|
|
182 |
global $wp_object_cache;
|
183 |
|
184 |
+
return $wp_object_cache->replace( $key, $value, $group, $expiration );
|
185 |
}
|
186 |
|
187 |
/**
|
194 |
* @param string $group The group value appended to the $key.
|
195 |
* @param int $expiration The expiration time, defaults to 0.
|
196 |
*
|
|
|
|
|
197 |
* @return bool Returns TRUE on success or FALSE on failure.
|
198 |
*/
|
199 |
+
function wp_cache_set( $key, $value, $group = '', $expiration = 0 ) {
|
|
|
200 |
global $wp_object_cache;
|
201 |
|
202 |
+
return $wp_object_cache->set( $key, $value, $group, $expiration );
|
203 |
}
|
204 |
|
205 |
/**
|
209 |
*
|
210 |
* @param int $_blog_id Blog ID
|
211 |
*
|
|
|
|
|
212 |
* @return bool
|
213 |
*/
|
214 |
+
function wp_cache_switch_to_blog( $_blog_id ) {
|
|
|
215 |
global $wp_object_cache;
|
216 |
|
217 |
+
return $wp_object_cache->switch_to_blog( $_blog_id );
|
218 |
}
|
219 |
|
220 |
/**
|
222 |
*
|
223 |
* @param string|array $groups A group or an array of groups to add.
|
224 |
*
|
|
|
|
|
225 |
* @return void
|
226 |
*/
|
227 |
+
function wp_cache_add_global_groups( $groups ) {
|
|
|
228 |
global $wp_object_cache;
|
229 |
|
230 |
+
$wp_object_cache->add_global_groups( $groups );
|
231 |
}
|
232 |
|
233 |
/**
|
235 |
*
|
236 |
* @param string|array $groups A group or an array of groups to add.
|
237 |
*
|
|
|
|
|
238 |
* @return void
|
239 |
*/
|
240 |
+
function wp_cache_add_non_persistent_groups( $groups ) {
|
|
|
241 |
global $wp_object_cache;
|
242 |
|
243 |
+
$wp_object_cache->add_non_persistent_groups( $groups );
|
244 |
}
|
245 |
|
246 |
+
class WP_Object_Cache {
|
247 |
+
|
248 |
/**
|
249 |
* The Redis client.
|
250 |
*
|
260 |
private $redis_version = null;
|
261 |
|
262 |
/**
|
263 |
+
* Track if Redis is available.
|
264 |
*
|
265 |
* @var bool
|
266 |
*/
|
281 |
public $cache = array();
|
282 |
|
283 |
/**
|
284 |
+
* Holds the diagnostics values.
|
285 |
*
|
286 |
+
* @var array
|
287 |
*/
|
288 |
+
public $diagnostics = null;
|
289 |
+
|
290 |
+
/**
|
291 |
+
* Holds the error messages.
|
292 |
+
*
|
293 |
+
* @var array
|
294 |
+
*/
|
295 |
+
public $errors = array();
|
296 |
|
297 |
/**
|
298 |
* List of global groups.
|
331 |
*
|
332 |
* @var array
|
333 |
*/
|
334 |
+
public $ignored_groups = array(
|
335 |
+
'counts',
|
336 |
+
'plugins',
|
337 |
+
'themes',
|
338 |
+
);
|
339 |
|
340 |
/**
|
341 |
* Prefix used for global groups.
|
352 |
public $blog_prefix = '';
|
353 |
|
354 |
/**
|
355 |
+
* Track how many requests were found in cache.
|
356 |
*
|
357 |
* @var int
|
358 |
*/
|
359 |
public $cache_hits = 0;
|
360 |
|
361 |
/**
|
362 |
+
* Track how may requests were not cached.
|
363 |
*
|
364 |
* @var int
|
365 |
*/
|
366 |
public $cache_misses = 0;
|
367 |
|
368 |
+
/**
|
369 |
+
* Track how long request took.
|
370 |
+
*
|
371 |
+
* @var int
|
372 |
+
*/
|
373 |
+
public $cache_time = 0;
|
374 |
+
|
375 |
+
/**
|
376 |
+
* Track how may calls were made.
|
377 |
+
*
|
378 |
+
* @var int
|
379 |
+
*/
|
380 |
+
public $cache_calls = 0;
|
381 |
+
|
382 |
/**
|
383 |
* Instantiate the Redis class.
|
384 |
*
|
385 |
* @param bool $fail_gracefully
|
386 |
*/
|
387 |
+
public function __construct( $fail_gracefully = true ) {
|
|
|
388 |
global $blog_id, $table_prefix;
|
389 |
|
390 |
$this->fail_gracefully = $fail_gracefully;
|
391 |
|
392 |
+
if ( defined( 'WP_REDIS_GLOBAL_GROUPS' ) && is_array( WP_REDIS_GLOBAL_GROUPS ) ) {
|
393 |
+
$this->global_groups = array_map( [ $this, 'sanitize_key_part' ], WP_REDIS_GLOBAL_GROUPS );
|
394 |
+
}
|
395 |
+
|
396 |
+
if ( defined( 'WP_REDIS_IGNORED_GROUPS' ) && is_array( WP_REDIS_IGNORED_GROUPS ) ) {
|
397 |
+
$this->ignored_groups = array_map( [ $this, 'sanitize_key_part' ], WP_REDIS_IGNORED_GROUPS );
|
398 |
+
}
|
399 |
+
|
400 |
+
if ( defined( 'WP_REDIS_UNFLUSHABLE_GROUPS' ) && is_array( WP_REDIS_UNFLUSHABLE_GROUPS ) ) {
|
401 |
+
$this->unflushable_groups = array_map( [ $this, 'sanitize_key_part' ], WP_REDIS_UNFLUSHABLE_GROUPS );
|
402 |
+
}
|
403 |
+
|
404 |
+
$client = $this->determine_client();
|
405 |
+
$parameters = $this->build_parameters();
|
406 |
+
|
407 |
+
try {
|
408 |
+
switch ( $client ) {
|
409 |
+
case 'hhvm':
|
410 |
+
$this->connectUsingHHVM( $parameters );
|
411 |
+
break;
|
412 |
+
case 'phpredis':
|
413 |
+
$this->connectUsingPhpRedis( $parameters );
|
414 |
+
break;
|
415 |
+
case 'credis':
|
416 |
+
$this->connectUsingCredis( $parameters );
|
417 |
+
break;
|
418 |
+
case 'predis':
|
419 |
+
default:
|
420 |
+
$this->connectUsingPredis( $parameters );
|
421 |
+
break;
|
422 |
+
}
|
423 |
+
|
424 |
+
if ( defined( 'WP_REDIS_CLUSTER' ) ) {
|
425 |
+
$this->diagnostics[ 'ping' ] = $this->redis->ping( current( array_values( WP_REDIS_CLUSTER ) ) );
|
426 |
+
} else {
|
427 |
+
$this->diagnostics[ 'ping' ] = $this->redis->ping();
|
428 |
+
}
|
429 |
+
|
430 |
+
$this->fetch_info();
|
431 |
+
|
432 |
+
$this->redis_connected = true;
|
433 |
+
} catch ( Exception $exception ) {
|
434 |
+
$this->handle_exception( $exception );
|
435 |
+
}
|
436 |
+
|
437 |
+
// Assign global and blog prefixes for use with keys
|
438 |
+
if ( function_exists( 'is_multisite' ) ) {
|
439 |
+
$this->global_prefix = is_multisite() ? '' : $table_prefix;
|
440 |
+
$this->blog_prefix = is_multisite() ? $blog_id : $table_prefix;
|
441 |
+
}
|
442 |
+
}
|
443 |
+
|
444 |
+
/**
|
445 |
+
* Determine the Redis client.
|
446 |
+
*
|
447 |
+
* @return string
|
448 |
+
*/
|
449 |
+
protected function determine_client() {
|
450 |
+
$client = 'predis';
|
451 |
+
|
452 |
+
if ( class_exists( 'Redis' ) ) {
|
453 |
+
$client = defined( 'HHVM_VERSION' ) ? 'hhvm' : 'phpredis';
|
454 |
+
}
|
455 |
+
|
456 |
+
if ( defined( 'WP_REDIS_CLIENT' ) ) {
|
457 |
+
$client = (string) WP_REDIS_CLIENT;
|
458 |
+
$client = str_replace( 'pecl', 'phpredis', $client );
|
459 |
+
}
|
460 |
+
|
461 |
+
return trim( strtolower( $client ) );
|
462 |
+
}
|
463 |
+
|
464 |
+
/**
|
465 |
+
* Build the connection parameters from config constants.
|
466 |
+
*
|
467 |
+
* @return array
|
468 |
+
*/
|
469 |
+
protected function build_parameters() {
|
470 |
$parameters = array(
|
471 |
'scheme' => 'tcp',
|
472 |
'host' => '127.0.0.1',
|
473 |
'port' => 6379,
|
474 |
+
'database' => 0,
|
475 |
+
'timeout' => 1,
|
476 |
+
'read_timeout' => 1,
|
477 |
+
'retry_interval' => null,
|
478 |
+
);
|
479 |
+
|
480 |
+
$settings = array(
|
481 |
+
'scheme',
|
482 |
+
'host',
|
483 |
+
'port',
|
484 |
+
'path',
|
485 |
+
'password',
|
486 |
+
'database',
|
487 |
+
'timeout',
|
488 |
+
'read_timeout',
|
489 |
+
'retry_interval',
|
490 |
);
|
491 |
|
492 |
+
foreach ( $settings as $setting ) {
|
493 |
+
$constant = sprintf( 'WP_REDIS_%s', strtoupper( $setting ) );
|
494 |
|
495 |
+
if ( defined( $constant ) ) {
|
496 |
+
$parameters[ $setting ] = constant( $constant );
|
497 |
}
|
498 |
}
|
499 |
|
500 |
+
if ( isset( $parameters['password'] ) ) {
|
501 |
+
$password = $parameters['password'];
|
|
|
502 |
|
503 |
+
if ( is_null( $password ) || $password === '' ) {
|
504 |
+
unset( $parameters['password'] );
|
505 |
+
}
|
506 |
}
|
507 |
|
508 |
+
return $parameters;
|
509 |
+
}
|
|
|
510 |
|
511 |
+
/**
|
512 |
+
* Connect to Redis using the PhpRedis (PECL) extention.
|
513 |
+
*
|
514 |
+
* @param array $parameters
|
515 |
+
* @return void
|
516 |
+
*/
|
517 |
+
protected function connectUsingPhpRedis( $parameters ) {
|
518 |
+
$version = phpversion( 'redis' );
|
519 |
|
520 |
+
$this->diagnostics[ 'client' ] = sprintf( 'PhpRedis (v%s)', $version );
|
|
|
|
|
|
|
|
|
521 |
|
522 |
+
if ( defined( 'WP_REDIS_SHARDS' ) ) {
|
523 |
+
$this->redis = new RedisArray( array_values( WP_REDIS_SHARDS ) );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
524 |
|
525 |
+
$this->diagnostics[ 'shards' ] = WP_REDIS_SHARDS;
|
526 |
+
} elseif ( defined( 'WP_REDIS_CLUSTER' ) ) {
|
527 |
+
$args = [
|
528 |
+
'cluster' => array_values( WP_REDIS_CLUSTER ),
|
529 |
+
'timeout' => $parameters['timeout'],
|
530 |
+
'read_timeout' => $parameters['read_timeout'],
|
531 |
+
];
|
532 |
|
533 |
+
if ( isset( $parameters['password'] ) && version_compare( $version, '4.3.0', '>=' ) ) {
|
534 |
+
$args['password'] = $parameters['password'];
|
|
|
535 |
}
|
536 |
|
537 |
+
$this->redis = new RedisCluster( null, ...array_values( $args ) );
|
538 |
+
|
539 |
+
$this->diagnostics += $args;
|
540 |
+
} else {
|
541 |
+
$this->redis = new Redis();
|
542 |
+
|
543 |
+
$args = [
|
544 |
+
'host' => $parameters['host'],
|
545 |
+
'port' => $parameters['port'],
|
546 |
+
'timeout' => $parameters['timeout'],
|
547 |
+
null,
|
548 |
+
'retry_interval' => $parameters['retry_interval'],
|
549 |
+
];
|
550 |
+
|
551 |
+
if ( strcasecmp( 'tls', $parameters['scheme'] ) === 0 ) {
|
552 |
+
$args['host'] = sprintf(
|
553 |
+
'%s://%s',
|
554 |
+
$parameters['scheme'],
|
555 |
+
str_replace( 'tls://', '', $parameters['host'] )
|
556 |
);
|
557 |
+
}
|
558 |
|
559 |
+
if ( strcasecmp( 'unix', $parameters['scheme'] ) === 0 ) {
|
560 |
+
$args['host'] = $parameters['path'];
|
561 |
+
$args['port'] = null;
|
562 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
563 |
|
564 |
+
if ( version_compare( $version, '3.1.3', '>=' ) ) {
|
565 |
+
$args['read_timeout'] = $parameters['read_timeout'];
|
566 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
567 |
|
568 |
+
call_user_func_array( [ $this->redis, 'connect' ], array_values( $args ) );
|
|
|
|
|
|
|
569 |
|
570 |
+
if ( isset( $parameters['password'] ) ) {
|
571 |
+
$args['password'] = $parameters['password'];
|
572 |
+
$this->redis->auth( $parameters['password'] );
|
573 |
+
}
|
574 |
|
575 |
+
if ( isset( $parameters['database'] ) ) {
|
576 |
+
if ( ctype_digit( $parameters['database'] ) ) {
|
577 |
+
$parameters['database'] = intval( $parameters['database'] );
|
|
|
578 |
}
|
579 |
|
580 |
+
$args['database'] = $parameters['database'];
|
581 |
+
|
582 |
+
if ( $parameters['database'] ) {
|
583 |
+
$this->redis->select( $parameters['database'] );
|
584 |
}
|
585 |
}
|
586 |
|
587 |
+
$this->diagnostics += $args;
|
588 |
+
}
|
|
|
|
|
589 |
|
590 |
+
if ( defined( 'WP_REDIS_SERIALIZER' ) && ! empty( WP_REDIS_SERIALIZER ) ) {
|
591 |
+
$this->redis->setOption( Redis::OPT_SERIALIZER, WP_REDIS_SERIALIZER );
|
592 |
+
}
|
593 |
+
}
|
594 |
|
595 |
+
/**
|
596 |
+
* Connect to Redis using the Predis library.
|
597 |
+
*
|
598 |
+
* @param array $parameters
|
599 |
+
* @return void
|
600 |
+
*/
|
601 |
+
protected function connectUsingPredis( $parameters ) {
|
602 |
+
$client = 'Predis';
|
603 |
|
604 |
+
// Require PHP 5.4 or greater
|
605 |
+
if ( version_compare( PHP_VERSION, '5.4.0', '<' ) ) {
|
606 |
+
throw new Exception( 'Predis requires PHP 5.4 or newer.' );
|
607 |
+
}
|
608 |
|
609 |
+
// Load bundled Predis library
|
610 |
+
if ( ! class_exists( 'Predis\Client' ) ) {
|
611 |
+
$predis = sprintf(
|
612 |
+
'%s/redis-cache/dependencies/vendor/predis/predis/autoload.php',
|
613 |
+
defined( 'WP_PLUGIN_DIR' ) ? WP_PLUGIN_DIR : WP_CONTENT_DIR . '/plugins'
|
614 |
+
);
|
615 |
|
616 |
+
if ( ! require_once $predis ) {
|
617 |
+
throw new Exception(
|
618 |
+
'Predis library not found. Re-install Redis Cache plugin or delete the object-cache.php.'
|
619 |
+
);
|
620 |
+
}
|
621 |
+
}
|
622 |
|
623 |
+
$options = array();
|
624 |
+
|
625 |
+
if ( defined( 'WP_REDIS_SHARDS' ) ) {
|
626 |
+
$parameters = WP_REDIS_SHARDS;
|
627 |
+
} elseif ( defined( 'WP_REDIS_SENTINEL' ) ) {
|
628 |
+
$parameters = WP_REDIS_SERVERS;
|
629 |
+
$options['replication'] = 'sentinel';
|
630 |
+
$options['service'] = WP_REDIS_SENTINEL;
|
631 |
+
} elseif ( defined( 'WP_REDIS_SERVERS' ) ) {
|
632 |
+
$parameters = WP_REDIS_SERVERS;
|
633 |
+
$options['replication'] = true;
|
634 |
+
} elseif ( defined( 'WP_REDIS_CLUSTER' ) ) {
|
635 |
+
$parameters = WP_REDIS_CLUSTER;
|
636 |
+
$options['cluster'] = 'redis';
|
637 |
+
}
|
638 |
+
|
639 |
+
if ( isset( $parameters['read_timeout'] ) && $parameters['read_timeout'] ) {
|
640 |
+
$parameters['read_write_timeout'] = $parameters['read_timeout'];
|
641 |
+
}
|
642 |
|
643 |
+
foreach ( array( 'WP_REDIS_SERVERS', 'WP_REDIS_SHARDS', 'WP_REDIS_CLUSTER' ) as $constant ) {
|
644 |
+
if ( defined( $constant ) ) {
|
645 |
+
if ( $parameters['database'] ) {
|
646 |
+
$options['parameters']['database'] = $parameters['database'];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
647 |
}
|
648 |
|
649 |
+
if ( isset( $parameters['password'] ) ) {
|
650 |
+
$options['parameters']['password'] = WP_REDIS_PASSWORD;
|
|
|
|
|
651 |
}
|
652 |
+
}
|
653 |
+
}
|
654 |
+
|
655 |
+
$this->redis = new Predis\Client( $parameters, $options );
|
656 |
+
$this->redis->connect();
|
657 |
|
658 |
+
$this->diagnostics = array_merge(
|
659 |
+
[ 'client' => sprintf( '%s (v%s)', $client, Predis\Client::VERSION ) ],
|
660 |
+
$parameters,
|
661 |
+
$options
|
662 |
+
);
|
663 |
+
}
|
664 |
+
|
665 |
+
/**
|
666 |
+
* Connect to Redis using the Credis library.
|
667 |
+
*
|
668 |
+
* @param array $parameters
|
669 |
+
* @return void
|
670 |
+
*/
|
671 |
+
protected function connectUsingCredis( $parameters ) {
|
672 |
+
$client = 'Credis';
|
673 |
+
|
674 |
+
// Require PHP 5.4 or greater
|
675 |
+
if ( version_compare( PHP_VERSION, '5.4.0', '<' ) ) {
|
676 |
+
throw new Exception( 'Predis requires PHP 5.4 or newer.' );
|
677 |
+
}
|
678 |
|
679 |
+
$creds_path = sprintf(
|
680 |
+
'%s/redis-cache/dependencies/vendor/colinmollenhour/credis/',
|
681 |
+
defined( 'WP_PLUGIN_DIR' ) ? WP_PLUGIN_DIR : WP_CONTENT_DIR . '/plugins'
|
682 |
+
);
|
683 |
+
|
684 |
+
$to_load = [];
|
685 |
+
|
686 |
+
if ( ! class_exists( 'Credis_Client' ) ) {
|
687 |
+
$to_load[] = 'Client.php';
|
688 |
+
}
|
689 |
+
|
690 |
+
if (
|
691 |
+
( defined( 'WP_REDIS_SHARDS' ) || defined( 'WP_REDIS_SENTINEL' ) || defined( 'WP_REDIS_SERVERS' ) || defined( 'WP_REDIS_CLUSTER' ) ) &&
|
692 |
+
! class_exists( 'Credis_Cluster' )
|
693 |
+
) {
|
694 |
+
$to_load[] = 'Cluster.php';
|
695 |
+
|
696 |
+
if ( defined( 'WP_REDIS_SENTINEL' ) && ! class_exists( 'Credis_Sentinel' ) ) {
|
697 |
+
$to_load[] = 'Sentinel.php';
|
698 |
}
|
699 |
+
}
|
700 |
|
701 |
+
foreach ( $to_load as $sub_path ) {
|
702 |
+
$path = $creds_path . $sub_path;
|
703 |
+
|
704 |
+
if ( file_exists( $path ) ) {
|
705 |
+
require_once $path;
|
706 |
} else {
|
707 |
+
throw new Exception(
|
708 |
+
'Credis library not found. Re-install Redis Cache plugin or delete object-cache.php.'
|
709 |
+
);
|
710 |
}
|
711 |
+
}
|
712 |
|
713 |
+
if ( defined( 'WP_REDIS_SHARDS' ) ) {
|
714 |
+
throw new Exception(
|
715 |
+
'Sharding not supported by bundled Credis library. Please review your Redis Cache configuration.'
|
716 |
+
);
|
717 |
+
}
|
718 |
+
|
719 |
+
if ( defined( 'WP_REDIS_SENTINEL' ) ) {
|
720 |
+
if ( is_array( WP_REDIS_SERVERS ) && count( WP_REDIS_SERVERS ) > 1 ) {
|
721 |
+
throw new Exception(
|
722 |
+
'Multipe sentinel servers are not supported by the bundled Credis library. Please review your Redis Cache configurations.'
|
723 |
+
);
|
724 |
+
}
|
725 |
+
|
726 |
+
$connection_string = array_values( WP_REDIS_SERVERS )[0];
|
727 |
+
$sentinel = new Credis_Sentinel( new Credis_Client( $connection_string ) );
|
728 |
+
$this->redis = $sentinel->getCluster( WP_REDIS_SENTINEL );
|
729 |
+
$args['is_sentinel'] = true;
|
730 |
+
} elseif ( defined( 'WP_REDIS_CLUSTER' ) || defined( 'WP_REDIS_SERVERS' ) ) {
|
731 |
+
$parameters['db'] = $parameters['database'];
|
732 |
+
|
733 |
+
$is_cluster = defined( 'WP_REDIS_CLUSTER' );
|
734 |
+
$clients = $is_cluster ? WP_REDIS_CLUSTER : WP_REDIS_SERVERS;
|
735 |
+
|
736 |
+
foreach ( $clients as $index => &$connection_string ) {
|
737 |
+
$url_components = parse_url( $connection_string );
|
738 |
+
parse_str( $url_components['query'], $add_params );
|
739 |
+
|
740 |
+
if ( ! $is_cluster && isset( $add_params['alias'] ) ) {
|
741 |
+
$add_params['master'] = 'master' === $add_params['alias'];
|
742 |
}
|
743 |
|
744 |
+
$add_params['host'] = $url_components['host'];
|
745 |
+
|
746 |
+
if ( ! isset( $add_params['alias'] ) ) {
|
747 |
+
$add_params['alias'] = "redis-$index";
|
748 |
}
|
749 |
+
|
750 |
+
$connection_string = array_merge( $parameters, $add_params );
|
751 |
}
|
752 |
|
753 |
+
$this->redis = new Credis_Cluster( $clients );
|
754 |
+
|
755 |
+
$args = $clients;
|
756 |
+
$args[ $is_cluster ? 'is_cluster' : 'is_multi' ] = true;
|
757 |
+
} else {
|
758 |
+
$host = 'unix' === $parameters['scheme']
|
759 |
+
? $parameters['path']
|
760 |
+
: $parameters['host'];
|
761 |
+
|
762 |
+
$args = [
|
763 |
+
"{$parameters['scheme']}://{$host}",
|
764 |
+
$parameters['port'],
|
765 |
+
$parameters['timeout'],
|
766 |
+
'',
|
767 |
+
isset( $parameters['database'] ) ? $parameters['database'] : 0,
|
768 |
+
isset( $parameters['password'] ) ? $parameters['password'] : null,
|
769 |
+
];
|
770 |
+
|
771 |
+
$this->redis = new Credis_Client( ...$args );
|
772 |
}
|
773 |
|
774 |
+
// Credis uses phpredis if it detects it unless we force it to run standalone.
|
775 |
+
$this->redis->forceStandalone();
|
776 |
+
|
777 |
+
$this->redis->connect();
|
778 |
+
|
779 |
+
$this->diagnostics = array_merge(
|
780 |
+
[ 'client' => sprintf( '%s (v%s)', $client, Credis_Client::VERSION ) ],
|
781 |
+
$args
|
782 |
+
);
|
783 |
+
}
|
784 |
+
|
785 |
+
/**
|
786 |
+
* Connect to Redis using HHVM's Redis extention.
|
787 |
+
*
|
788 |
+
* @param array $parameters
|
789 |
+
* @return void
|
790 |
+
*/
|
791 |
+
protected function connectUsingHHVM( $parameters ) {
|
792 |
+
$this->redis = new Redis();
|
793 |
+
|
794 |
+
// Adjust host and port, if the scheme is `unix`
|
795 |
+
if ( strcasecmp( 'unix', $parameters['scheme'] ) === 0 ) {
|
796 |
+
$parameters['host'] = 'unix://' . $parameters['path'];
|
797 |
+
$parameters['port'] = 0;
|
798 |
+
}
|
799 |
+
|
800 |
+
$this->redis->connect(
|
801 |
+
$parameters['host'],
|
802 |
+
$parameters['port'],
|
803 |
+
$parameters['timeout'],
|
804 |
+
null,
|
805 |
+
$parameters['retry_interval']
|
806 |
+
);
|
807 |
+
|
808 |
+
if ( $parameters['read_timeout'] ) {
|
809 |
+
$this->redis->setOption( Redis::OPT_READ_TIMEOUT, $parameters['read_timeout'] );
|
810 |
+
}
|
811 |
+
|
812 |
+
if ( isset( $parameters['password'] ) ) {
|
813 |
+
$this->redis->auth( $parameters['password'] );
|
814 |
+
}
|
815 |
+
|
816 |
+
if ( isset( $parameters['database'] ) ) {
|
817 |
+
if ( ctype_digit( $parameters['database'] ) ) {
|
818 |
+
$parameters['database'] = intval( $parameters['database'] );
|
819 |
+
}
|
820 |
+
|
821 |
+
if ( $parameters['database'] ) {
|
822 |
+
$this->redis->select( $parameters['database'] );
|
823 |
+
}
|
824 |
+
}
|
825 |
+
|
826 |
+
$this->diagnostics = array_merge(
|
827 |
+
[ 'client' => sprintf( 'HHVM Extension (v%s)', HHVM_VERSION ) ],
|
828 |
+
$parameters
|
829 |
+
);
|
830 |
+
}
|
831 |
+
|
832 |
+
/**
|
833 |
+
* Fetches Redis `INFO` mostly for server version.
|
834 |
+
*
|
835 |
+
* @return void
|
836 |
+
*/
|
837 |
+
public function fetch_info() {
|
838 |
+
$options = method_exists( $this->redis, 'getOptions' )
|
839 |
+
? $this->redis->getOptions()
|
840 |
+
: new stdClass();
|
841 |
+
|
842 |
+
if ( isset( $options->replication ) && $options->replication ) {
|
843 |
+
return;
|
844 |
+
}
|
845 |
+
|
846 |
+
if ( defined( 'WP_REDIS_CLUSTER' ) ) {
|
847 |
+
$info = $this->redis->info( current( array_values( WP_REDIS_CLUSTER ) ) );
|
848 |
+
} else {
|
849 |
+
$info = $this->redis->info();
|
850 |
+
}
|
851 |
+
|
852 |
+
if ( isset( $info['redis_version'] ) ) {
|
853 |
+
$this->redis_version = $info['redis_version'];
|
854 |
+
} elseif ( isset( $info['Server']['redis_version'] ) ) {
|
855 |
+
$this->redis_version = $info['Server']['redis_version'];
|
856 |
}
|
857 |
}
|
858 |
|
861 |
*
|
862 |
* @return bool
|
863 |
*/
|
864 |
+
public function redis_status() {
|
865 |
+
return $this->redis_connected;
|
|
|
866 |
}
|
867 |
|
868 |
/**
|
870 |
*
|
871 |
* @return mixed
|
872 |
*/
|
873 |
+
public function redis_instance() {
|
874 |
+
return $this->redis;
|
|
|
875 |
}
|
876 |
|
877 |
/**
|
879 |
*
|
880 |
* @return null|string
|
881 |
*/
|
882 |
+
public function redis_version() {
|
883 |
+
return $this->redis_version;
|
|
|
884 |
}
|
885 |
|
886 |
/**
|
895 |
* @param int $expiration The expiration time, defaults to 0.
|
896 |
* @return bool Returns TRUE on success or FALSE on failure.
|
897 |
*/
|
898 |
+
public function add( $key, $value, $group = 'default', $expiration = 0 ) {
|
899 |
+
return $this->add_or_replace( true, $key, $value, $group, $expiration );
|
|
|
900 |
}
|
901 |
|
902 |
/**
|
911 |
* @param int $expiration The expiration time, defaults to 0.
|
912 |
* @return bool Returns TRUE on success or FALSE on failure.
|
913 |
*/
|
914 |
+
public function replace( $key, $value, $group = 'default', $expiration = 0 ) {
|
915 |
+
return $this->add_or_replace( false, $key, $value, $group, $expiration );
|
|
|
916 |
}
|
917 |
|
918 |
/**
|
927 |
* @param int $expiration The expiration time, defaults to 0.
|
928 |
* @return bool Returns TRUE on success or FALSE on failure.
|
929 |
*/
|
930 |
+
protected function add_or_replace( $add, $key, $value, $group = 'default', $expiration = 0 ) {
|
931 |
+
$cache_addition_suspended = function_exists( 'wp_suspend_cache_addition' )
|
|
|
932 |
? wp_suspend_cache_addition()
|
933 |
: false;
|
934 |
|
935 |
+
if ( $add && $cache_addition_suspended ) {
|
936 |
return false;
|
937 |
}
|
938 |
|
939 |
$result = true;
|
940 |
+
$derived_key = $this->build_key( $key, $group );
|
941 |
|
942 |
// save if group not excluded and redis is up
|
943 |
+
if ( ! $this->is_ignored_group( $group ) && $this->redis_status() ) {
|
944 |
try {
|
945 |
+
$expiration = apply_filters( 'redis_cache_expiration', $this->validate_expiration( $expiration ), $key, $group );
|
946 |
+
$start_time = microtime( true );
|
947 |
|
948 |
+
if ( $add ) {
|
949 |
+
$args = [ $derived_key, $this->maybe_serialize( $value ) ];
|
|
|
950 |
|
951 |
+
if ( $this->redis instanceof Predis\Client ) {
|
952 |
+
$args[] = 'nx';
|
953 |
|
954 |
+
if ( $expiration ) {
|
955 |
+
$args[] = 'ex';
|
956 |
+
$args[] = $expiration;
|
957 |
+
}
|
958 |
+
} else {
|
959 |
+
if ( $expiration ) {
|
960 |
+
$args[] = [
|
961 |
+
'nx',
|
962 |
+
'ex' => $expiration,
|
963 |
+
];
|
964 |
+
} else {
|
965 |
+
$args[] = [ 'nx' ];
|
966 |
+
}
|
967 |
+
}
|
968 |
+
|
969 |
+
$result = $this->parse_redis_response(
|
970 |
+
$this->redis->set( ...$args )
|
971 |
+
);
|
972 |
+
|
973 |
+
if ( ! $result ) {
|
974 |
+
return false;
|
975 |
+
}
|
976 |
+
} elseif ( $expiration ) {
|
977 |
+
$result = $this->parse_redis_response( $this->redis->setex( $derived_key, $expiration, $this->maybe_serialize( $value ) ) );
|
978 |
} else {
|
979 |
+
$result = $this->parse_redis_response( $this->redis->set( $derived_key, $this->maybe_serialize( $value ) ) );
|
980 |
}
|
981 |
+
|
982 |
+
$execute_time = microtime( true ) - $start_time;
|
983 |
+
|
984 |
+
$this->cache_calls++;
|
985 |
+
$this->cache_time += $execute_time;
|
986 |
+
} catch ( Exception $exception ) {
|
987 |
+
$this->handle_exception( $exception );
|
988 |
|
989 |
return false;
|
990 |
}
|
991 |
}
|
992 |
|
993 |
+
$exists = isset( $this->cache[ $derived_key ] );
|
994 |
|
995 |
+
if ( $add == $exists ) {
|
996 |
return false;
|
997 |
}
|
998 |
|
999 |
+
if ( $result ) {
|
1000 |
+
$this->add_to_internal_cache( $derived_key, $value );
|
1001 |
}
|
1002 |
|
1003 |
return $result;
|
1010 |
* @param string $group The group value appended to the $key.
|
1011 |
* @return bool Returns TRUE on success or FALSE on failure.
|
1012 |
*/
|
1013 |
+
public function delete( $key, $group = 'default' ) {
|
|
|
|
|
|
|
1014 |
$result = false;
|
1015 |
+
$derived_key = $this->build_key( $key, $group );
|
1016 |
|
1017 |
+
if ( isset( $this->cache[ $derived_key ] ) ) {
|
1018 |
+
unset( $this->cache[ $derived_key ] );
|
1019 |
$result = true;
|
1020 |
}
|
1021 |
|
1022 |
+
$start_time = microtime( true );
|
1023 |
+
|
1024 |
+
if ( $this->redis_status() && ! $this->is_ignored_group( $group ) ) {
|
1025 |
try {
|
1026 |
+
$result = $this->parse_redis_response( $this->redis->del( $derived_key ) );
|
1027 |
+
} catch ( Exception $exception ) {
|
1028 |
+
$this->handle_exception( $exception );
|
1029 |
|
1030 |
return false;
|
1031 |
}
|
1032 |
}
|
1033 |
|
1034 |
+
$execute_time = microtime( true ) - $start_time;
|
1035 |
+
|
1036 |
+
$this->cache_calls++;
|
1037 |
+
$this->cache_time += $execute_time;
|
1038 |
+
|
1039 |
+
if ( function_exists( 'do_action' ) ) {
|
1040 |
+
do_action( 'redis_object_cache_delete', $key, $group, $execute_time );
|
1041 |
}
|
1042 |
|
1043 |
return $result;
|
1045 |
|
1046 |
/**
|
1047 |
* Invalidate all items in the cache. If `WP_REDIS_SELECTIVE_FLUSH` is `true`,
|
1048 |
+
* only keys prefixed with the `WP_REDIS_PREFIX` are flushed.
|
1049 |
*
|
1050 |
* @param int $delay Number of seconds to wait before invalidating the items.
|
1051 |
* @return bool Returns TRUE on success or FALSE on failure.
|
1052 |
*/
|
1053 |
+
public function flush( $delay = 0 ) {
|
1054 |
+
$delay = abs( intval( $delay ) );
|
|
|
1055 |
|
1056 |
+
if ( $delay ) {
|
1057 |
+
sleep( $delay );
|
1058 |
}
|
1059 |
|
1060 |
$results = [];
|
1061 |
$this->cache = array();
|
1062 |
|
1063 |
+
if ( $this->redis_status() ) {
|
1064 |
+
$salt = defined( 'WP_REDIS_PREFIX' ) ? trim( WP_REDIS_PREFIX ) : null;
|
1065 |
+
$selective = defined( 'WP_REDIS_SELECTIVE_FLUSH' ) ? WP_REDIS_SELECTIVE_FLUSH : null;
|
1066 |
|
1067 |
+
$start_time = microtime( true );
|
1068 |
|
1069 |
+
if ( $salt && $selective ) {
|
1070 |
+
$script = $this->get_flush_closure( $salt );
|
1071 |
|
1072 |
+
if ( defined( 'WP_REDIS_CLUSTER' ) ) {
|
1073 |
try {
|
1074 |
+
foreach ( $this->redis->_masters() as $master ) {
|
1075 |
+
$redis = new Redis();
|
1076 |
+
$redis->connect( $master[0], $master[1] );
|
1077 |
+
$results[] = $this->parse_redis_response( $script() );
|
1078 |
+
unset( $redis );
|
1079 |
}
|
1080 |
+
} catch ( Exception $exception ) {
|
1081 |
+
$this->handle_exception( $exception );
|
1082 |
|
1083 |
return false;
|
1084 |
}
|
1085 |
} else {
|
1086 |
try {
|
1087 |
+
$results[] = $this->parse_redis_response( $script() );
|
1088 |
+
} catch ( Exception $exception ) {
|
1089 |
+
$this->handle_exception( $exception );
|
1090 |
|
1091 |
return false;
|
1092 |
}
|
1093 |
}
|
1094 |
} else {
|
1095 |
+
if ( defined( 'WP_REDIS_CLUSTER' ) ) {
|
1096 |
try {
|
1097 |
+
foreach ( $this->redis->_masters() as $master ) {
|
1098 |
+
$results[] = $this->parse_redis_response( $this->redis->flushdb( $master ) );
|
1099 |
}
|
1100 |
+
} catch ( Exception $exception ) {
|
1101 |
+
$this->handle_exception( $exception );
|
1102 |
|
1103 |
return false;
|
1104 |
}
|
1105 |
} else {
|
1106 |
try {
|
1107 |
+
$results[] = $this->parse_redis_response( $this->redis->flushdb() );
|
1108 |
+
} catch ( Exception $exception ) {
|
1109 |
+
$this->handle_exception( $exception );
|
1110 |
|
1111 |
return false;
|
1112 |
}
|
1113 |
}
|
1114 |
}
|
1115 |
|
1116 |
+
if ( function_exists( 'do_action' ) ) {
|
1117 |
+
$execute_time = microtime( true ) - $start_time;
|
1118 |
|
1119 |
+
do_action( 'redis_object_cache_flush', $results, $delay, $selective, $salt, $execute_time );
|
1120 |
}
|
1121 |
}
|
1122 |
|
1123 |
+
if ( empty( $results ) ) {
|
1124 |
return false;
|
1125 |
}
|
1126 |
|
1127 |
+
foreach ( $results as $result ) {
|
1128 |
+
if ( ! $result ) {
|
1129 |
return false;
|
1130 |
}
|
1131 |
}
|
1136 |
/**
|
1137 |
* Returns a closure to flush selectively.
|
1138 |
*
|
1139 |
+
* @param string $salt The salt to be used to differentiate.
|
1140 |
* @return callable Generated callable executing the lua script.
|
1141 |
*/
|
1142 |
+
protected function get_flush_closure( $salt ) {
|
1143 |
+
if ( $this->unflushable_groups ) {
|
1144 |
+
return $this->lua_flush_extended_closure( $salt );
|
|
|
1145 |
} else {
|
1146 |
+
return $this->lua_flush_closure( $salt );
|
1147 |
}
|
1148 |
}
|
1149 |
|
1150 |
+
protected function glob_quote( $string ) {
|
1151 |
+
$characters = [ '*', '+', '?', '!', '{', '}', '[', ']', '(', ')', '|', '@' ];
|
1152 |
|
1153 |
return str_replace(
|
1154 |
$characters,
|
1155 |
+
array_map(
|
1156 |
+
function ( $character ) {
|
1157 |
+
return "[{$character}]";
|
1158 |
+
},
|
1159 |
+
$characters
|
1160 |
+
),
|
1161 |
$string
|
1162 |
);
|
1163 |
}
|
1165 |
/**
|
1166 |
* Returns a closure ready to be called to flush selectively ignoring unflushable groups.
|
1167 |
*
|
1168 |
+
* @param string $salt The salt to be used to differentiate.
|
1169 |
* @return callable Generated callable executing the lua script.
|
1170 |
*/
|
1171 |
+
protected function lua_flush_closure( $salt ) {
|
1172 |
+
$salt = $this->glob_quote( $salt );
|
|
|
1173 |
|
1174 |
+
return function () use ( $salt ) {
|
1175 |
$script = <<<LUA
|
1176 |
local cur = 0
|
1177 |
local i = 0
|
1189 |
return i
|
1190 |
LUA;
|
1191 |
|
1192 |
+
if ( version_compare( $this->redis_version(), '5', '<' ) && version_compare( $this->redis_version(), '3.2', '>=' ) ) {
|
1193 |
$script = 'redis.replicate_commands()' . "\n" . $script;
|
1194 |
}
|
1195 |
|
1196 |
+
$args = ( $this->redis instanceof Predis\Client )
|
1197 |
+
? [ $script, 0 ]
|
1198 |
+
: [ $script ];
|
1199 |
|
1200 |
+
return call_user_func_array( [ $this->redis, 'eval' ], $args );
|
1201 |
};
|
1202 |
}
|
1203 |
|
1204 |
/**
|
1205 |
* Returns a closure ready to be called to flush selectively.
|
1206 |
*
|
1207 |
+
* @param string $salt The salt to be used to differentiate.
|
1208 |
* @return callable Generated callable executing the lua script.
|
1209 |
*/
|
1210 |
+
protected function lua_flush_extended_closure( $salt ) {
|
1211 |
+
$salt = $this->glob_quote( $salt );
|
|
|
1212 |
|
1213 |
+
return function () use ( $salt ) {
|
1214 |
+
$salt_length = strlen( $salt );
|
1215 |
|
1216 |
+
$unflushable = array_map(
|
1217 |
+
function ( $group ) {
|
1218 |
+
return ":{$group}:";
|
1219 |
+
},
|
1220 |
+
$this->unflushable_groups
|
1221 |
+
);
|
1222 |
|
1223 |
$script = <<<LUA
|
1224 |
local cur = 0
|
1243 |
until 0 == cur
|
1244 |
return i
|
1245 |
LUA;
|
1246 |
+
if ( version_compare( $this->redis_version(), '5', '<' ) && version_compare( $this->redis_version(), '3.2', '>=' ) ) {
|
1247 |
$script = 'redis.replicate_commands()' . "\n" . $script;
|
1248 |
}
|
1249 |
|
1250 |
+
$args = ( $this->redis instanceof Predis\Client )
|
1251 |
+
? array_merge( [ $script, count( $unflushable ) ], $unflushable )
|
1252 |
+
: [ $script, $unflushable, count( $unflushable ) ];
|
1253 |
|
1254 |
+
return call_user_func_array( [ $this->redis, 'eval' ], $args );
|
1255 |
};
|
1256 |
}
|
1257 |
|
1260 |
*
|
1261 |
* Gets an object from cache based on $key and $group.
|
1262 |
*
|
1263 |
+
* @param string $key The key under which to store the value.
|
1264 |
+
* @param string $group The group value appended to the $key.
|
1265 |
+
* @param string $force Optional. Whether to force a refetch rather than relying on the local
|
1266 |
+
* cache. Default false.
|
1267 |
+
* @param bool &$found Optional. Whether the key was found in the cache. Disambiguates a return of
|
1268 |
+
* false, a storable value. Passed by reference. Default null.
|
1269 |
+
* @return bool|mixed Cached object value.
|
1270 |
*/
|
1271 |
+
public function get( $key, $group = 'default', $force = false, &$found = null ) {
|
1272 |
+
$derived_key = $this->build_key( $key, $group );
|
|
|
1273 |
|
1274 |
+
if ( isset( $this->cache[ $derived_key ] ) && ! $force ) {
|
|
|
|
|
1275 |
$found = true;
|
1276 |
$this->cache_hits++;
|
1277 |
|
1278 |
+
return $this->get_from_internal_cache( $derived_key );
|
1279 |
+
} elseif ( $this->is_ignored_group( $group ) || ! $this->redis_status() ) {
|
1280 |
$found = false;
|
1281 |
$this->cache_misses++;
|
1282 |
|
1283 |
return false;
|
1284 |
}
|
1285 |
|
1286 |
+
$start_time = microtime( true );
|
1287 |
+
|
1288 |
try {
|
1289 |
+
$result = $this->redis->get( $derived_key );
|
1290 |
+
} catch ( Exception $exception ) {
|
1291 |
+
$this->handle_exception( $exception );
|
1292 |
|
1293 |
return false;
|
1294 |
}
|
1295 |
|
1296 |
+
$execute_time = microtime( true ) - $start_time;
|
1297 |
+
|
1298 |
+
$this->cache_calls++;
|
1299 |
+
$this->cache_time += $execute_time;
|
1300 |
+
|
1301 |
+
if ( $result === null || $result === false ) {
|
1302 |
$found = false;
|
1303 |
$this->cache_misses++;
|
1304 |
|
1306 |
} else {
|
1307 |
$found = true;
|
1308 |
$this->cache_hits++;
|
1309 |
+
$value = $this->maybe_unserialize( $result );
|
1310 |
}
|
1311 |
|
1312 |
+
$this->add_to_internal_cache( $derived_key, $value );
|
|
|
|
|
|
|
1313 |
|
1314 |
+
if ( function_exists( 'do_action' ) ) {
|
1315 |
+
do_action( 'redis_object_cache_get', $key, $value, $group, $force, $found, $execute_time );
|
1316 |
}
|
1317 |
|
1318 |
+
if ( function_exists( 'apply_filters' ) && function_exists( 'has_filter' ) ) {
|
1319 |
+
if ( has_filter( 'redis_object_cache_get_value' ) ) {
|
1320 |
+
return apply_filters( 'redis_object_cache_get_value', $value, $key, $group, $force, $found );
|
1321 |
}
|
1322 |
}
|
1323 |
|
1325 |
}
|
1326 |
|
1327 |
/**
|
1328 |
+
* Retrieves multiple values from the cache in one call.
|
|
|
|
|
|
|
|
|
1329 |
*
|
1330 |
+
* @param array $keys Array of keys under which the cache contents are stored.
|
1331 |
+
* @param string $group Optional. Where the cache contents are grouped. Default empty.
|
1332 |
+
* @param bool $force Optional. Whether to force an update of the local cache
|
1333 |
+
* from the persistent cache. Default false.
|
1334 |
+
* @return array Array of values organized into groups.
|
1335 |
*/
|
1336 |
+
public function get_multiple( $keys, $group = 'default', $force = false ) {
|
1337 |
+
if ( ! is_array( $keys ) ) {
|
|
|
1338 |
return false;
|
1339 |
}
|
1340 |
|
|
|
1341 |
$cache = array();
|
1342 |
+
$derived_keys = array();
|
1343 |
|
1344 |
+
foreach ( $keys as $key ) {
|
1345 |
+
$derived_keys[ $key ] = $this->build_key( $key, $group );
|
1346 |
+
}
|
|
|
|
|
|
|
|
|
|
|
1347 |
|
1348 |
+
if ( $this->is_ignored_group( $group ) || ! $this->redis_status() ) {
|
1349 |
+
foreach ( $keys as $key ) {
|
1350 |
+
$cache[ $key ] = $this->get_from_internal_cache( $derived_keys[ $key ] );
|
1351 |
+
$cache[ $key ] === false ? $this->cache_misses++ : $this->cache_hits++;
|
1352 |
+
}
|
1353 |
|
1354 |
+
return $cache;
|
1355 |
+
}
|
|
|
|
|
|
|
|
|
|
|
1356 |
|
1357 |
+
$keys = array_values( $derived_keys );
|
|
|
1358 |
|
1359 |
+
if ( ! $force ) {
|
1360 |
+
foreach ( $keys as $key ) {
|
1361 |
+
$cache[ $key ] = $this->get_from_internal_cache( $derived_keys[ $key ] );
|
1362 |
+
}
|
1363 |
|
1364 |
+
$keys = array_keys(
|
1365 |
+
array_filter(
|
1366 |
+
$cache,
|
1367 |
+
function ( $value ) {
|
1368 |
+
return $value === false;
|
1369 |
+
}
|
1370 |
+
)
|
1371 |
+
);
|
1372 |
+
}
|
1373 |
|
1374 |
+
$start_time = microtime( true );
|
1375 |
+
|
1376 |
+
try {
|
1377 |
+
$results = array_combine( $keys, $this->redis->mget( $keys ) );
|
1378 |
+
} catch ( Exception $exception ) {
|
1379 |
+
$this->handle_exception( $exception );
|
1380 |
+
|
1381 |
+
$cache = array_fill( 0, count( $derived_keys ) - 1, false );
|
1382 |
}
|
1383 |
|
1384 |
+
$execute_time = microtime( true ) - $start_time;
|
1385 |
+
|
1386 |
+
$this->cache_calls++;
|
1387 |
+
$this->cache_time += $execute_time;
|
1388 |
+
|
1389 |
+
foreach ( $results as $key => $value ) {
|
1390 |
+
if ( $value === false ) {
|
1391 |
$this->cache_misses++;
|
1392 |
+
continue;
|
1393 |
+
}
|
1394 |
+
|
1395 |
+
$this->cache_hits++;
|
1396 |
+
|
1397 |
+
$cache[ $key ] = $value;
|
1398 |
+
$this->add_to_internal_cache( $derived_keys[ $key ], $value );
|
1399 |
+
}
|
1400 |
+
|
1401 |
+
$cache = array_map( array( $this, 'maybe_unserialize' ), $cache );
|
1402 |
+
|
1403 |
+
if ( function_exists( 'do_action' ) ) {
|
1404 |
+
do_action( 'redis_object_cache_get_multi', $keys, $cache, $group, $force, $execute_time );
|
1405 |
+
}
|
1406 |
+
|
1407 |
+
if ( function_exists( 'apply_filters' ) && function_exists( 'has_filter' ) ) {
|
1408 |
+
if ( has_filter( 'redis_object_cache_get_value' ) ) {
|
1409 |
+
foreach ( $cache as $key => $value ) {
|
1410 |
+
$cache[ $key ] = apply_filters( 'redis_object_cache_get_value', $value, $key, $group, $force );
|
1411 |
+
}
|
1412 |
}
|
1413 |
}
|
1414 |
|
1426 |
* @param int $expiration The expiration time, defaults to 0.
|
1427 |
* @return bool Returns TRUE on success or FALSE on failure.
|
1428 |
*/
|
1429 |
+
public function set( $key, $value, $group = 'default', $expiration = 0 ) {
|
|
|
|
|
|
|
1430 |
$result = true;
|
1431 |
+
$derived_key = $this->build_key( $key, $group );
|
1432 |
+
|
1433 |
+
$start_time = microtime( true );
|
1434 |
|
1435 |
// save if group not excluded from redis and redis is up
|
1436 |
+
if ( ! $this->is_ignored_group( $group ) && $this->redis_status() ) {
|
1437 |
+
$expiration = apply_filters( 'redis_cache_expiration', $this->validate_expiration( $expiration ), $key, $group );
|
1438 |
|
1439 |
try {
|
1440 |
+
if ( $expiration ) {
|
1441 |
+
$result = $this->parse_redis_response( $this->redis->setex( $derived_key, $expiration, $this->maybe_serialize( $value ) ) );
|
1442 |
} else {
|
1443 |
+
$result = $this->parse_redis_response( $this->redis->set( $derived_key, $this->maybe_serialize( $value ) ) );
|
1444 |
}
|
1445 |
+
} catch ( Exception $exception ) {
|
1446 |
+
$this->handle_exception( $exception );
|
1447 |
|
1448 |
return false;
|
1449 |
}
|
1450 |
+
|
1451 |
+
$this->cache_calls++;
|
1452 |
+
$this->cache_time += ( microtime( true ) - $start_time );
|
1453 |
}
|
1454 |
|
1455 |
// if the set was successful, or we didn't go to redis
|
1456 |
+
if ( $result ) {
|
1457 |
+
$this->add_to_internal_cache( $derived_key, $value );
|
1458 |
}
|
1459 |
|
1460 |
+
if ( function_exists( 'do_action' ) ) {
|
1461 |
+
$execute_time = microtime( true ) - $start_time;
|
1462 |
|
1463 |
+
do_action( 'redis_object_cache_set', $key, $value, $group, $expiration, $execute_time );
|
1464 |
}
|
1465 |
|
1466 |
return $result;
|
1474 |
* @param string $group
|
1475 |
* @return int|bool
|
1476 |
*/
|
1477 |
+
public function increment( $key, $offset = 1, $group = 'default' ) {
|
|
|
|
|
1478 |
$offset = (int) $offset;
|
1479 |
+
$derived_key = $this->build_key( $key, $group );
|
1480 |
|
1481 |
// If group is a non-Redis group, save to internal cache, not Redis
|
1482 |
+
if ( $this->is_ignored_group( $group ) || ! $this->redis_status() ) {
|
1483 |
+
$value = $this->get_from_internal_cache( $derived_key );
|
1484 |
$value += $offset;
|
1485 |
+
$this->add_to_internal_cache( $derived_key, $value );
|
1486 |
|
1487 |
return $value;
|
1488 |
}
|
1489 |
|
1490 |
+
$start_time = microtime( true );
|
1491 |
+
|
1492 |
try {
|
1493 |
+
$result = $this->parse_redis_response( $this->redis->incrBy( $derived_key, $offset ) );
|
1494 |
|
1495 |
+
$this->add_to_internal_cache( $derived_key, (int) $this->redis->get( $derived_key ) );
|
1496 |
+
} catch ( Exception $exception ) {
|
1497 |
+
$this->handle_exception( $exception );
|
1498 |
|
1499 |
return false;
|
1500 |
}
|
1501 |
|
1502 |
+
$execute_time = microtime( true ) - $start_time;
|
1503 |
+
|
1504 |
+
$this->cache_calls += 2;
|
1505 |
+
$this->cache_time += $execute_time;
|
1506 |
+
|
1507 |
return $result;
|
1508 |
}
|
1509 |
|
1515 |
* @param string $group
|
1516 |
* @return bool
|
1517 |
*/
|
1518 |
+
public function incr( $key, $offset = 1, $group = 'default' ) {
|
1519 |
+
return $this->increment( $key, $offset, $group );
|
|
|
1520 |
}
|
1521 |
|
1522 |
/**
|
1527 |
* @param string $group
|
1528 |
* @return int|bool
|
1529 |
*/
|
1530 |
+
public function decrement( $key, $offset = 1, $group = 'default' ) {
|
1531 |
+
$derived_key = $this->build_key( $key, $group );
|
|
|
1532 |
$offset = (int) $offset;
|
1533 |
|
1534 |
// If group is a non-Redis group, save to internal cache, not Redis
|
1535 |
+
if ( $this->is_ignored_group( $group ) || ! $this->redis_status() ) {
|
1536 |
+
$value = $this->get_from_internal_cache( $derived_key );
|
1537 |
$value -= $offset;
|
1538 |
+
$this->add_to_internal_cache( $derived_key, $value );
|
1539 |
|
1540 |
return $value;
|
1541 |
}
|
1542 |
|
1543 |
+
$start_time = microtime( true );
|
1544 |
+
|
1545 |
try {
|
1546 |
// Save to Redis
|
1547 |
+
$result = $this->parse_redis_response( $this->redis->decrBy( $derived_key, $offset ) );
|
1548 |
|
1549 |
+
$this->add_to_internal_cache( $derived_key, (int) $this->redis->get( $derived_key ) );
|
1550 |
+
} catch ( Exception $exception ) {
|
1551 |
+
$this->handle_exception( $exception );
|
1552 |
|
1553 |
return false;
|
1554 |
}
|
1555 |
|
1556 |
+
$execute_time = microtime( true ) - $start_time;
|
1557 |
+
|
1558 |
+
$this->cache_calls += 2;
|
1559 |
+
$this->cache_time += $execute_time;
|
1560 |
+
|
1561 |
return $result;
|
1562 |
}
|
1563 |
|
1566 |
*
|
1567 |
* @return string
|
1568 |
*/
|
1569 |
+
public function stats() {
|
1570 |
+
$bytes = array_map(
|
1571 |
+
function ( $key ) {
|
1572 |
+
return strlen( serialize( $key ) );
|
1573 |
+
},
|
1574 |
+
$this->cache
|
1575 |
+
);
|
1576 |
+
|
1577 |
+
?> <p>
|
1578 |
+
<strong>Redis Status:</strong>
|
1579 |
+
<?php echo $this->redis_status() ? 'Connected' : 'Not Connected'; ?>
|
1580 |
+
<br />
|
1581 |
+
<strong>Redis Client:</strong>
|
1582 |
+
<?php echo $this->diagnostics['client'] ?: 'Unknown'; ?>
|
1583 |
+
<br />
|
1584 |
+
<strong>Cache Hits:</strong>
|
1585 |
+
<?php echo $this->cache_hits; ?>
|
1586 |
+
<br />
|
1587 |
+
<strong>Cache Misses:</strong>
|
1588 |
+
<?php echo $this->cache_misses; ?>
|
1589 |
+
<br />
|
1590 |
+
<strong>Cache Size:</strong>
|
1591 |
+
<?php echo number_format( array_sum( $bytes ) / 1024, 2 ); ?> kB
|
1592 |
</p>
|
1593 |
+
<?php
|
1594 |
+
}
|
1595 |
|
1596 |
+
/**
|
1597 |
+
* Returns various information about the object cache.
|
1598 |
+
*
|
1599 |
+
* @return object
|
1600 |
+
*/
|
1601 |
+
public function info() {
|
1602 |
+
$total = $this->cache_hits + $this->cache_misses;
|
1603 |
+
|
1604 |
+
$bytes = array_map(
|
1605 |
+
function ( $keys ) {
|
1606 |
+
return strlen( serialize( $keys ) );
|
1607 |
+
},
|
1608 |
+
$this->cache
|
1609 |
+
);
|
1610 |
+
|
1611 |
+
return (object) [
|
1612 |
+
'hits' => $this->cache_hits,
|
1613 |
+
'misses' => $this->cache_misses,
|
1614 |
+
'ratio' => $total > 0 ? round( $this->cache_hits / ( $total / 100 ), 1 ) : 100,
|
1615 |
+
'bytes' => array_sum( $bytes ),
|
1616 |
+
'time' => $this->cache_time,
|
1617 |
+
'calls' => $this->cache_calls,
|
1618 |
+
'groups' => (object) [
|
1619 |
+
'global' => $this->global_groups,
|
1620 |
+
'non_persistent' => $this->ignored_groups,
|
1621 |
+
'unflushable' => $this->unflushable_groups,
|
1622 |
+
],
|
1623 |
+
'errors' => empty( $this->errors ) ? null : $this->errors,
|
1624 |
+
'meta' => [
|
1625 |
+
'Client' => $this->diagnostics['client'] ?: 'Unknown',
|
1626 |
+
'Redis Version' => $this->redis_version,
|
1627 |
+
],
|
1628 |
+
];
|
1629 |
}
|
1630 |
|
1631 |
/**
|
1636 |
*
|
1637 |
* @return string
|
1638 |
*/
|
1639 |
+
public function build_key( $key, $group = 'default' ) {
|
1640 |
+
if ( empty( $group ) ) {
|
|
|
1641 |
$group = 'default';
|
1642 |
}
|
1643 |
|
1644 |
+
$salt = defined( 'WP_REDIS_PREFIX' ) ? trim( WP_REDIS_PREFIX ) : '';
|
1645 |
+
$prefix = $this->is_global_group( $group ) ? $this->global_prefix : $this->blog_prefix;
|
1646 |
|
1647 |
+
$key = $this->sanitize_key_part( $key );
|
1648 |
+
$group = $this->sanitize_key_part( $group );
|
1649 |
|
1650 |
+
$prefix = trim( $prefix, '_-:$' );
|
1651 |
|
1652 |
return "{$salt}{$prefix}:{$group}:{$key}";
|
1653 |
}
|
1655 |
/**
|
1656 |
* Replaces the set group separator by another one
|
1657 |
*
|
1658 |
+
* @param string $part The string to sanitize.
|
1659 |
+
* @return string Sanitized string.
|
1660 |
*/
|
1661 |
+
protected function sanitize_key_part( $part ) {
|
1662 |
+
return str_replace( ':', '-', $part );
|
|
|
1663 |
}
|
1664 |
|
1665 |
/**
|
1666 |
* Checks if the given group is part the ignored group array
|
1667 |
*
|
1668 |
+
* @param string $group Name of the group to check
|
1669 |
* @return bool
|
1670 |
*/
|
1671 |
+
protected function is_ignored_group( $group ) {
|
1672 |
+
return in_array( $this->sanitize_key_part( $group ), $this->ignored_groups, true );
|
|
|
1673 |
}
|
1674 |
|
1675 |
/**
|
1676 |
* Checks if the given group is part the global group array
|
1677 |
*
|
1678 |
+
* @param string $group Name of the group to check
|
1679 |
* @return bool
|
1680 |
*/
|
1681 |
+
protected function is_global_group( $group ) {
|
1682 |
+
return in_array( $this->sanitize_key_part( $group ), $this->global_groups, true );
|
|
|
1683 |
}
|
1684 |
|
1685 |
/**
|
1686 |
* Checks if the given group is part the unflushable group array
|
1687 |
*
|
1688 |
+
* @param string $group Name of the group to check
|
1689 |
* @return bool
|
1690 |
*/
|
1691 |
+
protected function is_unflushable_group( $group ) {
|
1692 |
+
return in_array( $this->sanitize_key_part( $group ), $this->unflushable_groups, true );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1693 |
}
|
1694 |
|
1695 |
/**
|
1698 |
* @param mixed $response
|
1699 |
* @return mixed
|
1700 |
*/
|
1701 |
+
protected function parse_redis_response( $response ) {
|
1702 |
+
if ( is_bool( $response ) ) {
|
|
|
1703 |
return $response;
|
1704 |
}
|
1705 |
|
1706 |
+
if ( is_numeric( $response ) ) {
|
1707 |
return $response;
|
1708 |
}
|
1709 |
|
1710 |
+
if ( is_object( $response ) && method_exists( $response, 'getPayload' ) ) {
|
1711 |
return $response->getPayload() === 'OK';
|
1712 |
}
|
1713 |
|
1720 |
* @param string $derived_key Key to save value under.
|
1721 |
* @param mixed $value Object value.
|
1722 |
*/
|
1723 |
+
public function add_to_internal_cache( $derived_key, $value ) {
|
1724 |
+
if ( is_object( $value ) ) {
|
|
|
1725 |
$value = clone $value;
|
1726 |
}
|
1727 |
|
1728 |
+
$this->cache[ $derived_key ] = $value;
|
1729 |
}
|
1730 |
|
1731 |
/**
|
1732 |
* Get a value specifically from the internal, run-time cache, not Redis.
|
1733 |
*
|
1734 |
* @param int|string $derived_key Key value.
|
|
|
1735 |
*
|
1736 |
* @return bool|mixed Value on success; false on failure.
|
1737 |
*/
|
1738 |
+
public function get_from_internal_cache( $derived_key ) {
|
1739 |
+
if ( ! isset( $this->cache[ $derived_key ] ) ) {
|
|
|
1740 |
return false;
|
1741 |
}
|
1742 |
|
1743 |
+
if ( is_object( $this->cache[ $derived_key ] ) ) {
|
1744 |
+
return clone $this->cache[ $derived_key ];
|
1745 |
}
|
1746 |
|
1747 |
+
return $this->cache[ $derived_key ];
|
1748 |
}
|
1749 |
|
1750 |
/**
|
1753 |
* @param int $_blog_id
|
1754 |
* @return bool
|
1755 |
*/
|
1756 |
+
public function switch_to_blog( $_blog_id ) {
|
1757 |
+
if ( ! function_exists( 'is_multisite' ) || ! is_multisite() ) {
|
|
|
1758 |
return false;
|
1759 |
}
|
1760 |
|
1768 |
*
|
1769 |
* @param array $groups List of groups that are global.
|
1770 |
*/
|
1771 |
+
public function add_global_groups( $groups ) {
|
|
|
1772 |
$groups = (array) $groups;
|
1773 |
|
1774 |
+
if ( $this->redis_status() ) {
|
1775 |
+
$this->global_groups = array_unique( array_merge( $this->global_groups, $groups ) );
|
1776 |
} else {
|
1777 |
+
$this->ignored_groups = array_unique( array_merge( $this->ignored_groups, $groups ) );
|
1778 |
}
|
1779 |
}
|
1780 |
|
1781 |
/**
|
1782 |
* Sets the list of groups not to be cached by Redis.
|
1783 |
*
|
1784 |
+
* @param array $groups List of groups that are to be ignored.
|
1785 |
*/
|
1786 |
+
public function add_non_persistent_groups( $groups ) {
|
|
|
1787 |
$groups = (array) $groups;
|
1788 |
|
1789 |
+
$this->ignored_groups = array_unique( array_merge( $this->ignored_groups, $groups ) );
|
1790 |
}
|
1791 |
|
1792 |
/**
|
1794 |
*
|
1795 |
* @param array $groups List of groups that are unflushable.
|
1796 |
*/
|
1797 |
+
public function add_unflushable_groups( $groups ) {
|
|
|
1798 |
$groups = (array) $groups;
|
1799 |
|
1800 |
+
$this->unflushable_groups = array_unique( array_merge( $this->unflushable_groups, $groups ) );
|
1801 |
}
|
1802 |
|
1803 |
/**
|
1804 |
* Wrapper to validate the cache keys expiration value
|
1805 |
*
|
1806 |
+
* @param mixed $expiration Incoming expiration value (whatever it is)
|
1807 |
*/
|
1808 |
+
protected function validate_expiration( $expiration ) {
|
1809 |
+
$expiration = is_int( $expiration ) || ctype_digit( $expiration ) ? (int) $expiration : 0;
|
|
|
1810 |
|
1811 |
+
if ( defined( 'WP_REDIS_MAXTTL' ) ) {
|
1812 |
$max = (int) WP_REDIS_MAXTTL;
|
1813 |
|
1814 |
+
if ( $expiration === 0 || $expiration > $max ) {
|
1815 |
$expiration = $max;
|
1816 |
}
|
1817 |
}
|
1822 |
/**
|
1823 |
* Unserialize value only if it was serialized.
|
1824 |
*
|
1825 |
+
* @param string $original Maybe unserialized original, if is needed.
|
1826 |
+
* @return mixed Unserialized data can be any type.
|
1827 |
*/
|
1828 |
+
protected function maybe_unserialize( $original ) {
|
1829 |
+
if ( defined( 'WP_REDIS_SERIALIZER' ) && ! empty( WP_REDIS_SERIALIZER ) ) {
|
|
|
1830 |
return $original;
|
1831 |
}
|
1832 |
|
1833 |
+
if ( defined( 'WP_REDIS_IGBINARY' ) && WP_REDIS_IGBINARY && function_exists( 'igbinary_unserialize' ) ) {
|
1834 |
+
return igbinary_unserialize( $original );
|
1835 |
}
|
1836 |
|
1837 |
// don't attempt to unserialize data that wasn't serialized going in
|
1838 |
+
if ( $this->is_serialized( $original ) ) {
|
1839 |
+
$value = @unserialize( $original );
|
1840 |
|
1841 |
+
return is_object( $value ) ? clone $value : $value;
|
1842 |
}
|
1843 |
|
1844 |
return $original;
|
1847 |
/**
|
1848 |
* Serialize data, if needed.
|
1849 |
*
|
1850 |
+
* @param mixed $data Data that might be serialized.
|
1851 |
+
* @return mixed A scalar data
|
1852 |
*/
|
1853 |
+
protected function maybe_serialize( $data ) {
|
1854 |
+
if ( is_object( $data ) ) {
|
|
|
1855 |
$data = clone $data;
|
1856 |
}
|
1857 |
|
1858 |
+
if ( defined( 'WP_REDIS_SERIALIZER' ) && ! empty( WP_REDIS_SERIALIZER ) ) {
|
1859 |
return $data;
|
1860 |
}
|
1861 |
|
|