├── .gitignore ├── .travis.yml ├── LICENSE ├── README.rst ├── composer.json ├── src └── FreeAgent │ └── Bitter │ ├── Bitter.php │ ├── Date │ └── DatePeriod.php │ └── UnitOfTime │ ├── AbstractUnitOfTime.php │ ├── Day.php │ ├── Hour.php │ ├── Month.php │ ├── UnitOfTimeInterface.php │ ├── Week.php │ └── Year.php └── tests └── units ├── Bitter.php ├── Date └── DatePeriod.php └── UnitOfTime ├── AbstractUnitOfTime.php ├── Day.php ├── Hour.php ├── Month.php ├── Week.php └── Year.php /.gitignore: -------------------------------------------------------------------------------- 1 | vendor 2 | bin 3 | composer.lock 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 5.3 5 | - 5.4 6 | 7 | services: 8 | - redis-server 9 | 10 | before_script: 11 | # install phpredis extension. 12 | - sh -c "git clone https://github.com/nicolasff/phpredis/ && cd phpredis && phpize && ./configure && make && sudo make install " > /dev/null 13 | - echo "extension=redis.so" >> `php --ini | grep "Loaded Configuration" | sed -e "s|.*:\s*||"` 14 | - pecl install bitset 15 | # composer install 16 | - wget http://getcomposer.org/composer.phar 17 | - php composer.phar install --dev 18 | 19 | 20 | script: 21 | - bin/atoum -d tests/units 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Jérémy Romey 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Bitter Documentation 2 | ==================== 3 | 4 | .. image:: https://secure.travis-ci.org/jeremyFreeAgent/Bitter.png?branch=master 5 | :target: http://travis-ci.org/jeremyFreeAgent/Bitter 6 | 7 | **1.2.0 WORK IN PROGRESS** 8 | 9 | Bitter is a simple but powerful analytics library 10 | 11 | "Use Bitter and you have time to drink a bitter beer !" 12 | 13 | -- **Jérémy Romey** 14 | 15 | Bitter can answer following questions: 16 | 17 | * Has user X been online today? This week? This month? 18 | * Has user X performed action "Y"? 19 | * How many users have been active have this month? This hour? 20 | * How many unique users have performed action "Y" this week? 21 | * How many % of users that were active last week are still active? 22 | * How many % of users that were active last month are still active this month? 23 | 24 | Bitter is very easy to use and enables you to create your own reports easily - see the `Bitter Library `_ website for more info and documentation about this project. 25 | 26 | Installation 27 | ------------ 28 | Use `Composer `_ to install: ``free-agent/bitter``. 29 | 30 | In your `composer.json` you should have: 31 | 32 | .. code-block:: yaml 33 | 34 | { 35 | "require": { 36 | "free-agent/bitter": "1.1.*" 37 | } 38 | } 39 | 40 | Requirements 41 | ~~~~~~~~~~~~ 42 | Bitter uses `Redis `_ with version **>=2.6**. 43 | 44 | **Note**: Every key created in Redis will be prefixed by ``bitter:``, temp keys by ``bitter_temp:``. 45 | 46 | Bitter uses `Bitset PECL extension `_ with version **=1.0.1** for the ``getIds`` method. 47 | 48 | Basic usage 49 | ----------- 50 | Create a Bitter with a Redis client (Predis as example): 51 | 52 | .. code-block:: php 53 | 54 | $redisClient = new \Predis\Client(); 55 | $bitter = new \FreeAgent\Bitter\Bitter($redisClient); 56 | 57 | Mark user 123 as active and has played a song: 58 | 59 | .. code-block:: php 60 | 61 | $bitter 62 | ->mark('active', 123) 63 | ->mark('song:played', 123) 64 | ; 65 | 66 | **Note**: Please don't use huge ids (e.g. 2^32 or bigger) cause this will require large amounts of memory. 67 | 68 | Pass a DateTime as third argument: 69 | 70 | .. code-block:: php 71 | 72 | $bitter->mark('song:played', 123, new \DateTime('yesterday')); 73 | 74 | Test if user 123 has played a song this week: 75 | 76 | .. code-block:: php 77 | 78 | $currentWeek = new FreeAgent\Bitter\UnitOfTime\Week('song:played'); 79 | 80 | if ($bitter->in(123, $currentWeek) { 81 | echo 'User with id 123 has played a song this week.'; 82 | } else { 83 | echo 'User with id 123 has not played a song this week.'; 84 | } 85 | 86 | How many users were active yesterday: 87 | 88 | .. code-block:: php 89 | 90 | $yesterday = new \FreeAgent\Bitter\UnitOfTime\Day('active', new \DateTime('yesterday')); 91 | 92 | echo $bitter->count($yesterday) . ' users were active yesterday.'; 93 | 94 | Using BitOp 95 | ----------- 96 | How many users that were active yesterday are also active today: 97 | 98 | .. code-block:: php 99 | 100 | $today = new \FreeAgent\Bitter\UnitOfTime\Day('active'); 101 | $yesterday = new \FreeAgent\Bitter\UnitOfTime\Day('active', new \DateTime('yesterday')); 102 | 103 | $count = $bitter 104 | ->bitOpAnd('bit_op_example', $today, $yesterday) 105 | ->count('bit_op_example') 106 | ; 107 | echo $count . ' users were active yesterday and today.'; 108 | 109 | **Note**: The ``bit_op_example`` key will expire after 60 seconds. 110 | 111 | Test if user 123 was active yesterday and is active today: 112 | 113 | .. code-block:: php 114 | 115 | $today = new \FreeAgent\Bitter\UnitOfTime\Day('active'); 116 | $yesterday = new \FreeAgent\Bitter\UnitOfTime\Day('active', new \DateTime('yesterday')); 117 | 118 | $active = $bitter 119 | ->bitOpAnd('bit_op_example', $today, $yesterday) 120 | ->in(123, 'bit_op_example') 121 | ; 122 | if ($active) { 123 | echo 'User with id 123 was active yesterday and today.'; 124 | } else { 125 | echo 'User with id 123 was not active yesterday and today.'; 126 | } 127 | 128 | **Note**: Please look at `Redis BITOP Command `_ for performance considerations. 129 | 130 | Custom date period stats 131 | ------------------------ 132 | How many users that were active during a given date period: 133 | 134 | .. code-block:: php 135 | 136 | $from = new \DateTime('2010-14-02 20:15:30'); 137 | $to = new \DateTime('2012-21-12 13:30:45'); 138 | 139 | $count = $bitter 140 | ->bitDateRange('active', 'active_period_example', $from, $to) 141 | ->count('active_period_example') 142 | ; 143 | echo $count . ' users were active from "2010-14-02 20:15:30" to "2012-21-12 13:30:45".'; 144 | 145 | Get Ids for a given key 146 | ----------------------- 147 | Get Ids for a given date period: 148 | 149 | .. code-block:: php 150 | 151 | $from = new \DateTime('2010-14-02 20:15:30'); 152 | $to = new \DateTime('2012-21-12 13:30:45'); 153 | 154 | $ids = $bitter 155 | ->bitDateRange('active', 'active_period_example', $from, $to) 156 | ->getIds('active_period_example') 157 | ; 158 | echo 'Ids of users that were active from "2010-14-02 20:15:30" to "2012-21-12 13:30:45":'; 159 | echo '
'; 160 | echo implode(', ', $ids); 161 | 162 | Unit Tests 163 | ---------- 164 | 165 | You can run tests with: 166 | 167 | .. code-block:: sh 168 | 169 | bin/atoum -d tests/units 170 | 171 | Release notes 172 | ------------- 173 | 1.2.0 174 | 175 | * Added a remove method to remove a specific temp key. 176 | * Added a removeEvent method to remove all data of an event. 177 | * Renamed Event to UnitOfTime in order to be more explicit. 178 | 179 | 1.1.0 180 | 181 | * Added date period stats with bitDateRange method. 182 | 183 | Todo 184 | ---- 185 | * Implements the `Redis BITOP NOT Command `_. 186 | 187 | Thanks 188 | ------ 189 | This library is a port of `bitmapist `_ (Python) by `Amir Salihefendic `_. 190 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "free-agent/bitter", 3 | "description": "Bitter is a simple but powerful analytics library", 4 | "keywords": ["bitter", "analytics", "redis"], 5 | "homepage": "https://github.com/jeremyFreeAgent/Bitter", 6 | "type": "library", 7 | "license": "MIT", 8 | "authors": [ 9 | { 10 | "name": "Jérémy Romey", 11 | "email": "jeremy@free-agent.fr" 12 | } 13 | ], 14 | "support": { 15 | "issues": "https://github.com/jeremyFreeAgent/Bitter/issues" 16 | }, 17 | "require": { 18 | "php": ">=5.3.3", 19 | "ext-bitset": "1.0.1" 20 | }, 21 | "require-dev": { 22 | "predis/predis": "0.8.*", 23 | "atoum/atoum": "dev-master" 24 | }, 25 | "suggest": { 26 | "predis/predis": "0.8.*" 27 | }, 28 | "autoload": { 29 | "psr-0": { 30 | "FreeAgent\\Bitter": "src/" 31 | } 32 | }, 33 | "config": { 34 | "bin-dir": "bin" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/FreeAgent/Bitter/Bitter.php: -------------------------------------------------------------------------------- 1 | 17 | */ 18 | class Bitter 19 | { 20 | private $redisClient; 21 | private $prefixKey; 22 | private $prefixTempKey; 23 | private $expireTimeout; 24 | 25 | public function __construct($redisClient, $prefixKey = 'bitter:', $prefixTempKey = 'bitter_temp:', $expireTimeout = 60) 26 | { 27 | $this->setRedisClient($redisClient); 28 | $this->prefixKey = $prefixKey; 29 | $this->prefixTempKey = $prefixTempKey; 30 | $this->expireTimeout = $expireTimeout; 31 | } 32 | 33 | /** 34 | * Get the Redis client 35 | * 36 | * @return The Redis client 37 | */ 38 | public function getRedisClient() 39 | { 40 | return $this->redisClient; 41 | } 42 | 43 | /** 44 | * Set the Redis client 45 | * 46 | * @param object $newredisClient The Redis client 47 | */ 48 | public function setRedisClient($redisClient) 49 | { 50 | $this->redisClient = $redisClient; 51 | 52 | return $this; 53 | } 54 | 55 | /** 56 | * Marks an event for hours, days, weeks and months 57 | * 58 | * @param string $eventName The name of the event, could be "active" or "new_signups" 59 | * @param integer $id An unique id, typically user id. The id should not be huge, read Redis documentation why (bitmaps) 60 | * @param DateTime $dateTime Which date should be used as a reference point, default is now 61 | */ 62 | public function mark($eventName, $id, DateTime $dateTime = null) 63 | { 64 | $dateTime = is_null($dateTime) ? new DateTime : $dateTime; 65 | 66 | $eventData = array( 67 | new Year($eventName, $dateTime), 68 | new Month($eventName, $dateTime), 69 | new Week($eventName, $dateTime), 70 | new Day($eventName, $dateTime), 71 | new Hour($eventName, $dateTime), 72 | ); 73 | 74 | foreach ($eventData as $event) { 75 | $key = $this->prefixKey . $event->getKey(); 76 | $this->getRedisClient()->setbit($key, $id, 1); 77 | $this->getRedisClient()->sadd($this->prefixKey . 'keys', $key); 78 | } 79 | 80 | return $this; 81 | } 82 | 83 | /** 84 | * Makes it possible to see if an id has been marked 85 | * 86 | * @param integer $id An unique id 87 | * @param mixed $key The key or the event 88 | * @return boolean True if the id has been marked 89 | */ 90 | public function in($id, $key) 91 | { 92 | $key = $key instanceof UnitOfTimeInterface ? $this->prefixKey . $key->getKey() : $this->prefixTempKey . $key; 93 | 94 | return (bool) $this->getRedisClient()->getbit($key, $id); 95 | } 96 | 97 | /** 98 | * Counts the number of marks 99 | * 100 | * @param mixed $key The key or the event 101 | * @return integer The value of the count result 102 | */ 103 | public function count($key) 104 | { 105 | $key = $key instanceof UnitOfTimeInterface ? $this->prefixKey . $key->getKey() : $this->prefixTempKey . $key; 106 | 107 | return (int) $this->getRedisClient()->bitcount($key); 108 | } 109 | 110 | private function bitOp($op, $destKey, $keyOne, $keyTwo) 111 | { 112 | $keyOne = $keyOne instanceof UnitOfTimeInterface ? $this->prefixKey . $keyOne->getKey() : $this->prefixTempKey . $keyOne; 113 | $keyTwo = $keyTwo instanceof UnitOfTimeInterface ? $this->prefixKey . $keyTwo->getKey() : $this->prefixTempKey . $keyTwo; 114 | 115 | $this->getRedisClient()->bitop($op, $this->prefixTempKey . $destKey, $keyOne, $keyTwo); 116 | $this->getRedisClient()->sadd($this->prefixTempKey . 'keys', $destKey); 117 | $this->getRedisClient()->expire($destKey, $this->expireTimeout); 118 | 119 | return $this; 120 | } 121 | 122 | public function bitOpAnd($destKey, $keyOne, $keyTwo) 123 | { 124 | return $this->bitOp('AND', $destKey, $keyOne, $keyTwo); 125 | } 126 | 127 | public function bitOpOr($destKey, $keyOne, $keyTwo) 128 | { 129 | return $this->bitOp('OR', $destKey, $keyOne, $keyTwo); 130 | } 131 | 132 | public function bitOpXor($destKey, $keyOne, $keyTwo) 133 | { 134 | return $this->bitOp('XOR', $destKey, $keyOne, $keyTwo); 135 | } 136 | 137 | public function bitDateRange($key, $destKey, DateTime $from, DateTime $to) 138 | { 139 | if ($from > $to) { 140 | throw new Exception("DateTime from (" . $from->format('Y-m-d H:i:s') . ") must be anterior to DateTime to (" . $to->format('Y-m-d H:i:s') . ")."); 141 | } 142 | 143 | $this->getRedisClient()->del($this->prefixTempKey . $destKey); 144 | 145 | // Hours 146 | $hoursFrom = DatePeriod::createForHour($from, $to, DatePeriod::CREATE_FROM); 147 | foreach ($hoursFrom as $date) { 148 | $this->bitOpOr($destKey, new Hour($key, $date), $destKey); 149 | } 150 | $hoursTo = DatePeriod::createForHour($from, $to, DatePeriod::CREATE_TO); 151 | if (array_diff($hoursTo->toArray(true), $hoursFrom->toArray(true)) !== array_diff($hoursFrom->toArray(true), $hoursTo->toArray(true))) { 152 | foreach ($hoursTo as $date) { 153 | $this->bitOpOr($destKey, new Hour($key, $date), $destKey); 154 | } 155 | } 156 | 157 | // Days 158 | $daysFrom = DatePeriod::createForDay($from, $to, DatePeriod::CREATE_FROM); 159 | foreach ($daysFrom as $date) { 160 | $this->bitOpOr($destKey, new Day($key, $date), $destKey); 161 | } 162 | $daysTo = DatePeriod::createForDay($from, $to, DatePeriod::CREATE_TO); 163 | if (array_diff($daysTo->toArray(true), $daysFrom->toArray(true)) !== array_diff($daysFrom->toArray(true), $daysTo->toArray(true))) { 164 | foreach ($daysTo as $date) { 165 | $this->bitOpOr($destKey, new Day($key, $date), $destKey); 166 | } 167 | } 168 | 169 | // Months 170 | $monthsFrom = DatePeriod::createForMonth($from, $to, DatePeriod::CREATE_FROM); 171 | foreach ($monthsFrom as $date) { 172 | $this->bitOpOr($destKey, new Month($key, $date), $destKey); 173 | } 174 | $monthsTo = DatePeriod::createForMonth($from, $to, DatePeriod::CREATE_TO); 175 | if (array_diff($monthsTo->toArray(true), $monthsFrom->toArray(true)) !== array_diff($monthsFrom->toArray(true), $monthsTo->toArray(true))) { 176 | foreach ($monthsTo as $date) { 177 | $this->bitOpOr($destKey, new Month($key, $date), $destKey); 178 | } 179 | } 180 | 181 | // Years 182 | $years = DatePeriod::createForYear($from, $to); 183 | foreach ($years as $date) { 184 | $this->bitOpOr($destKey, new Year($key, $date), $destKey); 185 | } 186 | 187 | $this->getRedisClient()->sadd($this->prefixTempKey . 'keys', $destKey); 188 | $this->getRedisClient()->expire($destKey, $this->expireTimeout); 189 | 190 | return $this; 191 | } 192 | 193 | /** 194 | * Returns the ids of an key or event 195 | * 196 | * @param mixed $key The key or the event 197 | * @return array The ids array 198 | */ 199 | public function getIds($key) 200 | { 201 | $key = $key instanceof UnitOfTimeInterface ? $this->prefixKey . $key->getKey() : $this->prefixTempKey . $key; 202 | 203 | $string = $this->getRedisClient()->get($key); 204 | 205 | $data = $this->bitsetToString($string); 206 | 207 | $ids = array(); 208 | while (false !== ($pos = strpos($data, '1'))) { 209 | $data[$pos] = 0; 210 | $ids[] = (int)($pos/8)*8 + abs(7-($pos%8)); 211 | } 212 | 213 | sort($ids); 214 | 215 | return $ids; 216 | } 217 | 218 | protected function bitsetToString($bitset = '') 219 | { 220 | return bitset_to_string($bitset); 221 | } 222 | 223 | /** 224 | * Removes all Bitter keys 225 | */ 226 | public function removeAll() 227 | { 228 | $keys_chunk = array_chunk($this->getRedisClient()->smembers($this->prefixKey . 'keys'), 100); 229 | 230 | foreach ($keys_chunk as $keys) { 231 | $this->getRedisClient()->del($keys); 232 | } 233 | 234 | return $this; 235 | } 236 | 237 | /** 238 | * Removes all Bitter temp keys 239 | */ 240 | public function removeTemp() 241 | { 242 | $keys_chunk = array_chunk($this->getRedisClient()->smembers($this->prefixTempKey . 'keys'), 100); 243 | 244 | foreach ($keys_chunk as $keys) { 245 | $this->getRedisClient()->del($keys); 246 | } 247 | 248 | return $this; 249 | } 250 | } 251 | -------------------------------------------------------------------------------- /src/FreeAgent/Bitter/Date/DatePeriod.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | class DatePeriod extends \DatePeriod 12 | { 13 | const CREATE_FROM = 'from'; 14 | const CREATE_TO = 'to'; 15 | 16 | public function toArray($dateToString = false) 17 | { 18 | $dates = array(); 19 | foreach ($this as $date) { 20 | $dates[] = true === $dateToString ? $date->format('Y-m-d H:i:s') : $date; 21 | } 22 | 23 | return $dates; 24 | } 25 | 26 | public static function createForHour(DateTime $from, DateTime $to, $fromOrTo = self::CREATE_FROM) 27 | { 28 | if ($from->format('Y-m-d') != $to->format('Y-m-d')) { 29 | if (self::CREATE_TO !== $fromOrTo) { 30 | $from->setTime($from->format('H'), 0, 0); 31 | $to = clone($from); 32 | $to->setTime(24, 0, 0); 33 | } else { 34 | $from = clone($to); 35 | $from->setTime(0, 0, 0); 36 | $to->setTime($to->format('H'), 0, 0); 37 | } 38 | } else { 39 | $from->setTime($from->format('H'), 0, 0); 40 | $to->setTime($to->format('H'), 0, 0); 41 | } 42 | 43 | return new DatePeriod($from, new DateInterval('PT1H'), $to); 44 | } 45 | 46 | public static function createForDay(DateTime $from, DateTime $to, $fromOrTo = self::CREATE_FROM) 47 | { 48 | $mFrom = $from; 49 | $mTo = $to; 50 | if ($mFrom->format('Y-m') != $mTo->format('Y-m')) { 51 | if (self::CREATE_TO !== $fromOrTo) { 52 | $mFrom->setTime(0, 0, 0); 53 | $mFrom->setDate($mFrom->format('Y'), $mFrom->format('m'), $mFrom->format('d')); 54 | $mTo = clone($mFrom); 55 | $mTo->setDate($mFrom->format('Y'), $mFrom->format('m') + 1, 1); 56 | } else { 57 | $mTo->setTime(0, 0, 0); 58 | $mFrom = clone($mTo); 59 | $mFrom->setDate($mFrom->format('Y'), $mFrom->format('m'), 1); 60 | $mTo->setDate($mTo->format('Y'), $mTo->format('m'), $mTo->format('d')); 61 | } 62 | } else { 63 | $mFrom->setTime(0, 0, 0); 64 | $mTo->setTime(0, 0, 0); 65 | } 66 | 67 | return new DatePeriod($mFrom, new DateInterval('P1D'), $mTo, self::CREATE_TO !== $fromOrTo || $from->format('Y-m') == $to->format('Y-m') ? self::EXCLUDE_START_DATE : null); 68 | } 69 | 70 | public static function createForMonth(DateTime $from, DateTime $to, $fromOrTo = self::CREATE_FROM) 71 | { 72 | $mFrom = $from; 73 | $mTo = $to; 74 | if ($mFrom->format('Y') != $to->format('Y')) { 75 | if (self::CREATE_TO !== $fromOrTo) { 76 | $mFrom->setTime(0, 0, 0); 77 | $mFrom->setDate($mFrom->format('Y'), $mFrom->format('m'), 1); 78 | $mTo = clone($mFrom); 79 | $mTo->setDate($mFrom->format('Y'), 13, 1); 80 | } else { 81 | $mTo->setTime(0, 0, 0); 82 | $mFrom = clone($mTo); 83 | $mFrom->setDate($mFrom->format('Y'), 1, 1); 84 | $mTo->setDate($mTo->format('Y'), $mTo->format('m'), 1); 85 | } 86 | } else { 87 | $mFrom->setDate($mFrom->format('Y'), $mFrom->format('m'), 1); 88 | $mFrom->setTime(0, 0, 0); 89 | $mTo->setDate($mTo->format('Y'), $mTo->format('m'), 1); 90 | $mTo->setTime(0, 0, 0); 91 | } 92 | 93 | return new DatePeriod($mFrom, new DateInterval('P1M'), $mTo, self::CREATE_TO !== $fromOrTo || $from->format('Y') == $to->format('Y') ? self::EXCLUDE_START_DATE : null); 94 | } 95 | 96 | public static function createForYear(DateTime $from, DateTime $to) 97 | { 98 | $from->setDate($from->format('Y'), 1, 1); 99 | $from->setTime(0, 0, 0); 100 | $to->setDate($to->format('Y'), 1, 1); 101 | $to->setTime(0, 0, 0); 102 | 103 | return new DatePeriod($from, new DateInterval('P1Y'), $to, self::EXCLUDE_START_DATE); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/FreeAgent/Bitter/UnitOfTime/AbstractUnitOfTime.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | abstract class AbstractUnitOfTime 11 | { 12 | protected $eventName; 13 | protected $dateTime; 14 | 15 | public function __construct($eventName, DateTime $dateTime = null) 16 | { 17 | $this->eventName = $eventName; 18 | $this->dateTime = is_null($dateTime) ? new DateTime : $dateTime; 19 | } 20 | 21 | public function getUnitOfTimeName() 22 | { 23 | return $this->eventName; 24 | } 25 | 26 | public function getDateTime() 27 | { 28 | return $this->dateTime; 29 | } 30 | 31 | abstract public function getDateTimeFormated(); 32 | 33 | public function getKey() 34 | { 35 | return sprintf('%s:%s', $this->getUnitOfTimeName(), $this->getDateTimeFormated()); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/FreeAgent/Bitter/UnitOfTime/Day.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | class Day extends AbstractUnitOfTime implements UnitOfTimeInterface 9 | { 10 | public function getDateTimeFormated() 11 | { 12 | return sprintf('%s-%s-%s', $this->getDateTime()->format('Y'), $this->getDateTime()->format('m'), $this->getDateTime()->format('d')); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/FreeAgent/Bitter/UnitOfTime/Hour.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | class Hour extends AbstractUnitOfTime implements UnitOfTimeInterface 9 | { 10 | public function getDateTimeFormated() 11 | { 12 | return sprintf('%s-%s-%s-%s', $this->getDateTime()->format('Y'), $this->getDateTime()->format('m'), $this->getDateTime()->format('d'), $this->getDateTime()->format('H')); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/FreeAgent/Bitter/UnitOfTime/Month.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | class Month extends AbstractUnitOfTime implements UnitOfTimeInterface 9 | { 10 | public function getDateTimeFormated() 11 | { 12 | return sprintf('%s-%s', $this->getDateTime()->format('Y'), $this->getDateTime()->format('m')); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/FreeAgent/Bitter/UnitOfTime/UnitOfTimeInterface.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | interface UnitOfTimeInterface 9 | { 10 | public function __construct($eventName, \DateTime $dateTime); 11 | public function getUnitOfTimeName(); 12 | public function getDateTime(); 13 | public function getDateTimeFormated(); 14 | public function getKey(); 15 | } 16 | -------------------------------------------------------------------------------- /src/FreeAgent/Bitter/UnitOfTime/Week.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | class Week extends AbstractUnitOfTime implements UnitOfTimeInterface 9 | { 10 | public function getDateTimeFormated() 11 | { 12 | return sprintf('%s-W%s', $this->getDateTime()->format('Y'), $this->getDateTime()->format('W')); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/FreeAgent/Bitter/UnitOfTime/Year.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | class Year extends AbstractUnitOfTime implements UnitOfTimeInterface 9 | { 10 | public function getDateTimeFormated() 11 | { 12 | return sprintf('%s', $this->getDateTime()->format('Y')); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /tests/units/Bitter.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | class Bitter extends atoum\test 17 | { 18 | public function dataProviderTestedClients() 19 | { 20 | $clients = array( 21 | new \Predis\Client(), 22 | ); 23 | 24 | if (class_exists('\Redis')) { 25 | $conn = new \Redis(); 26 | $conn->connect('127.0.0.1'); 27 | 28 | $clients[] = $conn; 29 | } 30 | 31 | return $clients; 32 | } 33 | 34 | private function getPrefixKey() 35 | { 36 | return 'test_bitter:'; 37 | } 38 | 39 | private function getPrefixTempKey() 40 | { 41 | return 'test_bitter_temp:'; 42 | } 43 | 44 | private function removeAll($redisClient) 45 | { 46 | $keys_chunk = array_chunk($redisClient->keys($this->getPrefixKey() . '*'), 100); 47 | 48 | foreach ($keys_chunk as $keys) { 49 | $redisClient->del($keys); 50 | } 51 | 52 | $keys_chunk = array_chunk($redisClient->keys($this->getPrefixTempKey() . '*'), 100); 53 | 54 | foreach ($keys_chunk as $keys) { 55 | $redisClient->del($keys); 56 | } 57 | } 58 | 59 | /** 60 | * @dataProvider dataProviderTestedClients 61 | */ 62 | public function testConstruct($redisClient) 63 | { 64 | $bitter = new TestedBitter($redisClient, $this->getPrefixKey(), $this->getPrefixTempKey()); 65 | 66 | $this 67 | ->object($bitter->getRedisClient()) 68 | ->isIdenticalTo($redisClient) 69 | ; 70 | } 71 | 72 | /** 73 | * @dataProvider dataProviderTestedClients 74 | */ 75 | public function testMarkUnitOfTime($redisClient) 76 | { 77 | $bitter = new TestedBitter($redisClient, $this->getPrefixKey(), $this->getPrefixTempKey()); 78 | 79 | $this->removeAll($redisClient); 80 | 81 | $dateTime = DateTime::createFromFormat('Y-m-d H:i:s', '2012-11-06 15:30:45'); 82 | 83 | $day = new Day('drink_a_bitter_beer', $dateTime); 84 | 85 | $this 86 | ->integer($bitter->count($day)) 87 | ->isIdenticalTo(0) 88 | ; 89 | $this 90 | ->boolean($bitter->in(404, $day)) 91 | ->isFalse() 92 | ; 93 | 94 | $this 95 | ->object($bitter->mark('drink_a_bitter_beer', 404, $dateTime)) 96 | ->isIdenticalTo($bitter) 97 | ; 98 | 99 | $this 100 | ->integer($bitter->count($day)) 101 | ->isIdenticalTo(1) 102 | ; 103 | $this 104 | ->boolean($bitter->in(404, $day)) 105 | ->isTrue() 106 | ; 107 | 108 | // Adding it a second time with the same dateTime ! 109 | $bitter->mark('drink_a_bitter_beer', 404, $dateTime); 110 | 111 | $this 112 | ->integer($bitter->count($day)) 113 | ->isIdenticalTo(1) 114 | ; 115 | $this 116 | ->boolean($bitter->in(404, $day)) 117 | ->isTrue() 118 | ; 119 | 120 | $this->removeAll($redisClient); 121 | 122 | $day = new Day('drink_a_bitter_beer', new DateTime()); 123 | $this 124 | ->boolean($bitter->in(13, $day)) 125 | ->isFalse() 126 | ; 127 | $bitter->mark('drink_a_bitter_beer', 13); 128 | $this 129 | ->boolean($bitter->in(13, $day)) 130 | ->isTrue() 131 | ; 132 | 133 | $this->removeAll($redisClient); 134 | } 135 | 136 | /** 137 | * @dataProvider dataProviderTestedClients 138 | */ 139 | public function testbitOpAnd($redisClient) 140 | { 141 | $bitter = new TestedBitter($redisClient, $this->getPrefixKey(), $this->getPrefixTempKey()); 142 | 143 | $this->removeAll($redisClient); 144 | 145 | $yesterday = new Day('drink_a_bitter_beer', new DateTime('yesterday')); 146 | $today = new Day('drink_a_bitter_beer', new DateTime('today')); 147 | 148 | $bitter->mark('drink_a_bitter_beer', 13, new DateTime('today')); 149 | $bitter->mark('drink_a_bitter_beer', 13, new DateTime('yesterday')); 150 | $bitter->mark('drink_a_bitter_beer', 404, new DateTime('yesterday')); 151 | 152 | $this 153 | ->object($bitter->bitOpAnd('test_a', $today, $yesterday)) 154 | ->isIdenticalTo($bitter) 155 | ; 156 | 157 | $this 158 | ->boolean($bitter->bitOpAnd('test_b', $today, $yesterday)->in(13, 'test_b')) 159 | ->isTrue() 160 | ; 161 | 162 | $this 163 | ->boolean($bitter->bitOpAnd('test_c', $today, $yesterday)->in(404, 'test_c')) 164 | ->isFalse() 165 | ; 166 | 167 | $this->removeAll($redisClient); 168 | } 169 | 170 | /** 171 | * @dataProvider dataProviderTestedClients 172 | */ 173 | public function testbitOpOr($redisClient) 174 | { 175 | $bitter = new TestedBitter($redisClient, $this->getPrefixKey(), $this->getPrefixTempKey()); 176 | 177 | $this->removeAll($redisClient); 178 | 179 | $twoDaysAgo = new Day('drink_a_bitter_beer', new DateTime('2 days ago')); 180 | $yesterday = new Day('drink_a_bitter_beer', new DateTime('yesterday')); 181 | $today = new Day('drink_a_bitter_beer', new DateTime('today')); 182 | 183 | $bitter->mark('drink_a_bitter_beer', 13, new DateTime('today')); 184 | $bitter->mark('drink_a_bitter_beer', 13, new DateTime('yesterday')); 185 | $bitter->mark('drink_a_bitter_beer', 404, new DateTime('yesterday')); 186 | 187 | $this 188 | ->object($bitter->bitOpOr('test_a', $today, $yesterday)) 189 | ->isIdenticalTo($bitter) 190 | ; 191 | 192 | $this 193 | ->boolean($bitter->bitOpOr('test_b', $today, $yesterday)->in(13, 'test_b')) 194 | ->isTrue() 195 | ; 196 | 197 | $this 198 | ->boolean($bitter->bitOpOr('test_c', $today, $twoDaysAgo)->in(13, 'test_c')) 199 | ->isTrue() 200 | ; 201 | 202 | $this 203 | ->boolean($bitter->bitOpOr('test_d', $today, $twoDaysAgo)->in(404, 'test_d')) 204 | ->isFalse() 205 | ; 206 | 207 | $this->removeAll($redisClient); 208 | } 209 | 210 | /** 211 | * @dataProvider dataProviderTestedClients 212 | */ 213 | public function testbitOpXor($redisClient) 214 | { 215 | $bitter = new TestedBitter($redisClient, $this->getPrefixKey(), $this->getPrefixTempKey()); 216 | 217 | $this->removeAll($redisClient); 218 | 219 | $yesterday = new Day('drink_a_bitter_beer', new DateTime('yesterday')); 220 | $today = new Day('drink_a_bitter_beer', new DateTime('today')); 221 | 222 | $bitter->mark('drink_a_bitter_beer', 13, new DateTime('today')); 223 | $bitter->mark('drink_a_bitter_beer', 13, new DateTime('yesterday')); 224 | $bitter->mark('drink_a_bitter_beer', 404, new DateTime('yesterday')); 225 | 226 | $this 227 | ->object($bitter->bitOpXor('test_a', $today, $yesterday)) 228 | ->isIdenticalTo($bitter) 229 | ; 230 | 231 | $this 232 | ->boolean($bitter->bitOpXor('test_b', $today, $yesterday)->in(13, 'test_b')) 233 | ->isFalse() 234 | ; 235 | 236 | $this 237 | ->boolean($bitter->bitOpXor('test_c', $today, $yesterday)->in(404, 'test_c')) 238 | ->isTrue() 239 | ; 240 | 241 | $this->removeAll($redisClient); 242 | } 243 | 244 | /** 245 | * @dataProvider dataProviderTestedClients 246 | */ 247 | public function testBitDateRange($redisClient) 248 | { 249 | $bitter = new TestedBitter($redisClient, $this->getPrefixKey(), $this->getPrefixTempKey()); 250 | 251 | $this->removeAll($redisClient); 252 | 253 | $dateTime = DateTime::createFromFormat('Y-m-d H:i:s', '2011-11-06 15:30:45'); 254 | $bitter->mark('drink_a_bitter_beer', 1, $dateTime); 255 | $dateTime = DateTime::createFromFormat('Y-m-d H:i:s', '2012-10-12 15:30:45'); 256 | $bitter->mark('drink_a_bitter_beer', 2, $dateTime); 257 | 258 | $this 259 | ->if($from = DateTime::createFromFormat('Y-m-d H:i:s', '2012-10-05 15:30:45')) 260 | ->and($to = DateTime::createFromFormat('Y-m-d H:i:s', '2012-12-07 15:30:45')) 261 | ->and($bitter->bitDateRange('drink_a_bitter_beer', 'test_create_date_period', $from, $to)) 262 | ->then() 263 | ->object($bitter->bitDateRange('drink_a_bitter_beer', 'test_create_date_period', $from, $to)) 264 | ->isIdenticalTo($bitter) 265 | ; 266 | 267 | $this 268 | ->if($prefixKey = $this->getPrefixKey()) 269 | ->and($prefixTempKey = $this->getPrefixTempKey()) 270 | ->exception( 271 | function() use ($redisClient, $prefixKey, $prefixTempKey) { 272 | $bitter = new TestedBitter($redisClient, $prefixKey, $prefixTempKey); 273 | $from = DateTime::createFromFormat('Y-m-d H:i:s', '2012-12-07 15:30:45'); 274 | $to = DateTime::createFromFormat('Y-m-d H:i:s', '2012-12-07 14:30:45'); 275 | $bitter->bitDateRange('drink_a_bitter_beer', 'test_create_date_period', $from, $to); 276 | } 277 | ) 278 | ->hasMessage("DateTime from (2012-12-07 15:30:45) must be anterior to DateTime to (2012-12-07 14:30:45).") 279 | ; 280 | 281 | $this 282 | ->if($from = DateTime::createFromFormat('Y-m-d H:i:s', '2010-10-05 20:30:45')) 283 | ->and($to = DateTime::createFromFormat('Y-m-d H:i:s', '2012-12-07 12:30:45')) 284 | ->and($bitter->bitDateRange('drink_a_bitter_beer', 'test_create_date_period', $from, $to)) 285 | ->then() 286 | ->boolean($bitter->in(1, 'test_create_date_period')) 287 | ->isTrue() 288 | ->boolean($bitter->in(2, 'test_create_date_period')) 289 | ->isTrue() 290 | ->integer($bitter->count('test_create_date_period')) 291 | ->isEqualTo(2) 292 | ; 293 | 294 | $this 295 | ->if($from = DateTime::createFromFormat('Y-m-d H:i:s', '2012-09-05 20:30:45')) 296 | ->and($to = DateTime::createFromFormat('Y-m-d H:i:s', '2012-12-07 12:30:45')) 297 | ->and($bitter->bitDateRange('drink_a_bitter_beer', 'test_create_date_period', $from, $to)) 298 | ->then() 299 | ->boolean($bitter->in(1, 'test_create_date_period')) 300 | ->isFalse() 301 | ->boolean($bitter->in(2, 'test_create_date_period')) 302 | ->isTrue() 303 | ->integer($bitter->count('test_create_date_period')) 304 | ->isEqualTo(1) 305 | ; 306 | 307 | $this->removeAll($redisClient); 308 | } 309 | 310 | /** 311 | * @dataProvider dataProviderTestedClients 312 | */ 313 | public function testGetIds($redisClient) 314 | { 315 | $bitter = new TestedBitter($redisClient, $this->getPrefixKey(), $this->getPrefixTempKey()); 316 | 317 | $this->removeAll($redisClient); 318 | 319 | $eventKey = 'liloo_multipass'; 320 | $dateTime = new DateTime('2012-10-12 15:30:45'); 321 | $event = new Day($eventKey, $dateTime); 322 | $ids = array(1, 13, 404, 2, 12700042, 13003, 99); 323 | 324 | foreach ($ids as $id) { 325 | $bitter->mark($eventKey, $id, $dateTime); 326 | } 327 | sort($ids); 328 | 329 | $this 330 | ->array($bitter->getIds($event)) 331 | ->isIdenticalTo($ids) 332 | ; 333 | 334 | $this->removeAll($redisClient); 335 | } 336 | 337 | /** 338 | * @dataProvider dataProviderTestedClients 339 | */ 340 | public function testRemoveAll($redisClient) 341 | { 342 | $this->removeAll($redisClient); 343 | 344 | $this 345 | ->array($redisClient->keys($this->getPrefixKey() . '*')) 346 | ->isEmpty() 347 | ; 348 | 349 | $bitter = new TestedBitter($redisClient, $this->getPrefixKey(), $this->getPrefixTempKey()); 350 | 351 | $yesterday = new Day('drink_a_bitter_beer', new DateTime('yesterday')); 352 | $today = new Day('drink_a_bitter_beer', new DateTime('today')); 353 | 354 | $bitter->mark('drink_a_bitter_beer', 13, new DateTime('today')); 355 | $bitter->mark('drink_a_bitter_beer', 13, new DateTime('yesterday')); 356 | $bitter->mark('drink_a_bitter_beer', 404, new DateTime('yesterday')); 357 | 358 | $this 359 | ->array($redisClient->keys($this->getPrefixKey() . '*')) 360 | ->isNotEmpty() 361 | ; 362 | 363 | $this 364 | ->object($bitter->removeAll()) 365 | ->isIdenticalTo($bitter) 366 | ; 367 | 368 | $this 369 | ->array($redisClient->keys($this->getPrefixKey() . '*')) 370 | ->strictlyContains($this->getPrefixKey() . 'keys') 371 | ; 372 | 373 | 374 | $this->removeAll($redisClient); 375 | } 376 | 377 | /** 378 | * @dataProvider dataProviderTestedClients 379 | */ 380 | public function testRemoveTemp($redisClient) 381 | { 382 | $keys_chunk = array_chunk($redisClient->keys($this->getPrefixKey() . '*'), 100); 383 | 384 | foreach ($keys_chunk as $keys) { 385 | $redisClient->del($keys); 386 | } 387 | 388 | $this 389 | ->array($redisClient->keys($this->getPrefixKey() . '*')) 390 | ->isEmpty() 391 | ; 392 | 393 | $bitter = new TestedBitter($redisClient, $this->getPrefixKey(), $this->getPrefixTempKey()); 394 | 395 | $yesterday = new Day('drink_a_bitter_beer', new DateTime('yesterday')); 396 | $today = new Day('drink_a_bitter_beer', new DateTime('today')); 397 | 398 | $bitter->mark('drink_a_bitter_beer', 13, new DateTime('today')); 399 | $bitter->mark('drink_a_bitter_beer', 13, new DateTime('yesterday')); 400 | $bitter->mark('drink_a_bitter_beer', 404, new DateTime('yesterday')); 401 | 402 | $bitter->bitOpOr('test_b', $today, $yesterday); 403 | 404 | $this 405 | ->array($redisClient->keys($this->getPrefixKey() . '*')) 406 | ->isNotEmpty() 407 | ; 408 | 409 | $this 410 | ->array($redisClient->keys($this->getPrefixTempKey() . '*')) 411 | ->isNotEmpty() 412 | ; 413 | 414 | $this 415 | ->object($bitter->removeTemp()) 416 | ->isIdenticalTo($bitter) 417 | ; 418 | 419 | $this 420 | ->array($redisClient->keys($this->getPrefixTempKey() . '*')) 421 | ->strictlyContains($this->getPrefixTempKey() . 'keys') 422 | ; 423 | 424 | $this 425 | ->array($redisClient->keys($this->getPrefixKey() . '*')) 426 | ->isNotEmpty() 427 | ; 428 | 429 | // Expire timeout 430 | $this->removeAll($redisClient); 431 | 432 | $bitter = new TestedBitter($redisClient, $this->getPrefixKey(), $this->getPrefixTempKey(), 2); 433 | $bitter->mark('drink_a_bitter_beer', 13, new DateTime('today')); 434 | $bitter->mark('drink_a_bitter_beer', 13, new DateTime('yesterday')); 435 | $bitter->mark('drink_a_bitter_beer', 404, new DateTime('yesterday')); 436 | 437 | $bitter->bitOpOr('test_b', $today, $yesterday); 438 | 439 | $this 440 | ->array($redisClient->keys($this->getPrefixTempKey() . '*')) 441 | ->isNotEmpty() 442 | ; 443 | 444 | sleep(3); 445 | 446 | $this 447 | ->array($redisClient->keys($this->getPrefixTempKey() . '*')) 448 | ->strictlyContains($this->getPrefixTempKey() . 'keys') 449 | ; 450 | 451 | $this->removeAll($redisClient); 452 | } 453 | } 454 | -------------------------------------------------------------------------------- /tests/units/Date/DatePeriod.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class DatePeriod extends atoum\test 16 | { 17 | public function testToArray() 18 | { 19 | $from = DateTime::createFromFormat('Y-m-d H:i:s', '2012-11-13 15:30:45'); 20 | $to = DateTime::createFromFormat('Y-m-d H:i:s', '2012-11-20 15:30:45'); 21 | 22 | $datePeriodArray = array( 23 | '2012-11-14 15:30:45', 24 | '2012-11-15 15:30:45', 25 | '2012-11-16 15:30:45', 26 | '2012-11-17 15:30:45', 27 | '2012-11-18 15:30:45', 28 | '2012-11-19 15:30:45', 29 | ); 30 | 31 | $this 32 | ->if($datePeriod = new TestedDatePeriod($from, new DateInterval('P1D'), $to, TestedDatePeriod::EXCLUDE_START_DATE)) 33 | ->array($datePeriod->toArray(true)) 34 | ->isEqualTo($datePeriodArray) 35 | ; 36 | } 37 | 38 | public function testCreateForHour() 39 | { 40 | $from = DateTime::createFromFormat('Y-m-d H:i:s', '2012-11-15 10:30:45'); 41 | $to = DateTime::createFromFormat('Y-m-d H:i:s', '2012-11-15 15:30:45'); 42 | 43 | $datePeriodFromArray = $datePeriodToArray = array( 44 | '2012-11-15 10:00:00', 45 | '2012-11-15 11:00:00', 46 | '2012-11-15 12:00:00', 47 | '2012-11-15 13:00:00', 48 | '2012-11-15 14:00:00', 49 | ); 50 | 51 | $this 52 | ->if($datePeriod = TestedDatePeriod::createForHour($from, $to, TestedDatePeriod::CREATE_FROM)) 53 | ->array($datePeriod->toArray(true)) 54 | ->isEqualTo($datePeriodFromArray) 55 | ->if($datePeriod = TestedDatePeriod::createForHour($from, $to, TestedDatePeriod::CREATE_TO)) 56 | ->array($datePeriod->toArray(true)) 57 | ->isEqualTo($datePeriodToArray) 58 | ; 59 | 60 | $from = DateTime::createFromFormat('Y-m-d H:i:s', '2012-11-13 15:30:45'); 61 | $to = DateTime::createFromFormat('Y-m-d H:i:s', '2012-11-15 10:30:45'); 62 | 63 | $datePeriodFromArray = array( 64 | '2012-11-13 15:00:00', 65 | '2012-11-13 16:00:00', 66 | '2012-11-13 17:00:00', 67 | '2012-11-13 18:00:00', 68 | '2012-11-13 19:00:00', 69 | '2012-11-13 20:00:00', 70 | '2012-11-13 21:00:00', 71 | '2012-11-13 22:00:00', 72 | '2012-11-13 23:00:00', 73 | ); 74 | 75 | $datePeriodToArray = array( 76 | '2012-11-15 00:00:00', 77 | '2012-11-15 01:00:00', 78 | '2012-11-15 02:00:00', 79 | '2012-11-15 03:00:00', 80 | '2012-11-15 04:00:00', 81 | '2012-11-15 05:00:00', 82 | '2012-11-15 06:00:00', 83 | '2012-11-15 07:00:00', 84 | '2012-11-15 08:00:00', 85 | '2012-11-15 09:00:00', 86 | ); 87 | 88 | $this 89 | ->if($datePeriod = TestedDatePeriod::createForHour($from, $to, TestedDatePeriod::CREATE_FROM)) 90 | ->array($datePeriod->toArray(true)) 91 | ->isEqualTo($datePeriodFromArray) 92 | ->if($datePeriod = TestedDatePeriod::createForHour($from, $to, TestedDatePeriod::CREATE_TO)) 93 | ->array($datePeriod->toArray(true)) 94 | ->isEqualTo($datePeriodToArray) 95 | ; 96 | } 97 | 98 | public function testCreateForDay() 99 | { 100 | $from = DateTime::createFromFormat('Y-m-d H:i:s', '2012-11-13 10:30:45'); 101 | $to = DateTime::createFromFormat('Y-m-d H:i:s', '2012-11-13 15:30:45'); 102 | 103 | $datePeriodFromArray = $datePeriodToArray = array(); 104 | 105 | $this 106 | ->if($datePeriod = TestedDatePeriod::createForDay($from, $to, TestedDatePeriod::CREATE_FROM)) 107 | ->array($datePeriod->toArray(true)) 108 | ->isEqualTo($datePeriodFromArray) 109 | ->if($datePeriod = TestedDatePeriod::createForDay($from, $to, TestedDatePeriod::CREATE_TO)) 110 | ->array($datePeriod->toArray(true)) 111 | ->isEqualTo($datePeriodToArray) 112 | ; 113 | 114 | $from = DateTime::createFromFormat('Y-m-d H:i:s', '2012-11-10 15:30:45'); 115 | $to = DateTime::createFromFormat('Y-m-d H:i:s', '2012-11-15 10:30:45'); 116 | 117 | $datePeriodFromArray = $datePeriodToArray = array( 118 | '2012-11-11 00:00:00', 119 | '2012-11-12 00:00:00', 120 | '2012-11-13 00:00:00', 121 | '2012-11-14 00:00:00', 122 | ); 123 | 124 | $this 125 | ->if($datePeriod = TestedDatePeriod::createForDay($from, $to, TestedDatePeriod::CREATE_FROM)) 126 | ->array($datePeriod->toArray(true)) 127 | ->isEqualTo($datePeriodFromArray) 128 | ->if($datePeriod = TestedDatePeriod::createForDay($from, $to, TestedDatePeriod::CREATE_TO)) 129 | ->array($datePeriod->toArray(true)) 130 | ->isEqualTo($datePeriodToArray) 131 | ; 132 | 133 | $from = DateTime::createFromFormat('Y-m-d H:i:s', '2012-08-20 15:30:45'); 134 | $to = DateTime::createFromFormat('Y-m-d H:i:s', '2012-11-10 10:30:45'); 135 | 136 | $datePeriodFromArray = array( 137 | '2012-08-21 00:00:00', 138 | '2012-08-22 00:00:00', 139 | '2012-08-23 00:00:00', 140 | '2012-08-24 00:00:00', 141 | '2012-08-25 00:00:00', 142 | '2012-08-26 00:00:00', 143 | '2012-08-27 00:00:00', 144 | '2012-08-28 00:00:00', 145 | '2012-08-29 00:00:00', 146 | '2012-08-30 00:00:00', 147 | '2012-08-31 00:00:00', 148 | ); 149 | 150 | $datePeriodToArray = array( 151 | '2012-11-01 00:00:00', 152 | '2012-11-02 00:00:00', 153 | '2012-11-03 00:00:00', 154 | '2012-11-04 00:00:00', 155 | '2012-11-05 00:00:00', 156 | '2012-11-06 00:00:00', 157 | '2012-11-07 00:00:00', 158 | '2012-11-08 00:00:00', 159 | '2012-11-09 00:00:00', 160 | ); 161 | 162 | $this 163 | ->if($datePeriod = TestedDatePeriod::createForDay($from, $to, TestedDatePeriod::CREATE_FROM)) 164 | ->array($datePeriod->toArray(true)) 165 | ->isEqualTo($datePeriodFromArray) 166 | ->if($datePeriod = TestedDatePeriod::createForDay($from, $to, TestedDatePeriod::CREATE_TO)) 167 | ->array($datePeriod->toArray(true)) 168 | ->isEqualTo($datePeriodToArray) 169 | ; 170 | } 171 | 172 | public function testCreateForMonth() 173 | { 174 | $from = DateTime::createFromFormat('Y-m-d H:i:s', '2012-11-13 10:30:45'); 175 | $to = DateTime::createFromFormat('Y-m-d H:i:s', '2012-11-13 15:30:45'); 176 | 177 | $datePeriodFromArray = $datePeriodToArray = array(); 178 | 179 | $this 180 | ->if($datePeriod = TestedDatePeriod::createForMonth($from, $to, TestedDatePeriod::CREATE_FROM)) 181 | ->array($datePeriod->toArray(true)) 182 | ->isEqualTo($datePeriodFromArray) 183 | ->if($datePeriod = TestedDatePeriod::createForMonth($from, $to, TestedDatePeriod::CREATE_TO)) 184 | ->array($datePeriod->toArray(true)) 185 | ->isEqualTo($datePeriodToArray) 186 | ; 187 | 188 | $from = DateTime::createFromFormat('Y-m-d H:i:s', '2012-11-10 10:30:45'); 189 | $to = DateTime::createFromFormat('Y-m-d H:i:s', '2012-11-13 15:30:45'); 190 | 191 | $datePeriodFromArray = $datePeriodToArray = array(); 192 | 193 | $this 194 | ->if($datePeriod = TestedDatePeriod::createForMonth($from, $to, TestedDatePeriod::CREATE_FROM)) 195 | ->array($datePeriod->toArray(true)) 196 | ->isEqualTo($datePeriodFromArray) 197 | ->if($datePeriod = TestedDatePeriod::createForMonth($from, $to, TestedDatePeriod::CREATE_TO)) 198 | ->array($datePeriod->toArray(true)) 199 | ->isEqualTo($datePeriodToArray) 200 | ; 201 | 202 | $from = DateTime::createFromFormat('Y-m-d H:i:s', '2012-10-10 15:30:45'); 203 | $to = DateTime::createFromFormat('Y-m-d H:i:s', '2012-11-15 10:30:45'); 204 | 205 | $datePeriodFromArray = $datePeriodToArray = array(); 206 | 207 | $this 208 | ->if($datePeriod = TestedDatePeriod::createForMonth($from, $to, TestedDatePeriod::CREATE_FROM)) 209 | ->array($datePeriod->toArray(true)) 210 | ->isEqualTo($datePeriodFromArray) 211 | ->if($datePeriod = TestedDatePeriod::createForMonth($from, $to, TestedDatePeriod::CREATE_TO)) 212 | ->array($datePeriod->toArray(true)) 213 | ->isEqualTo($datePeriodToArray) 214 | ; 215 | 216 | $from = DateTime::createFromFormat('Y-m-d H:i:s', '2012-09-10 15:30:45'); 217 | $to = DateTime::createFromFormat('Y-m-d H:i:s', '2012-11-15 10:30:45'); 218 | 219 | $datePeriodFromArray = $datePeriodToArray = array( 220 | '2012-10-01 00:00:00', 221 | ); 222 | 223 | $this 224 | ->if($datePeriod = TestedDatePeriod::createForMonth($from, $to, TestedDatePeriod::CREATE_FROM)) 225 | ->array($datePeriod->toArray(true)) 226 | ->isEqualTo($datePeriodFromArray) 227 | ->if($datePeriod = TestedDatePeriod::createForMonth($from, $to, TestedDatePeriod::CREATE_TO)) 228 | ->array($datePeriod->toArray(true)) 229 | ->isEqualTo($datePeriodToArray) 230 | ; 231 | 232 | $from = DateTime::createFromFormat('Y-m-d H:i:s', '2012-08-20 15:30:45'); 233 | $to = DateTime::createFromFormat('Y-m-d H:i:s', '2012-11-10 10:30:45'); 234 | 235 | $datePeriodFromArray = $datePeriodToArray = array( 236 | '2012-09-01 00:00:00', 237 | '2012-10-01 00:00:00', 238 | ); 239 | 240 | $this 241 | ->if($datePeriod = TestedDatePeriod::createForMonth($from, $to, TestedDatePeriod::CREATE_FROM)) 242 | ->array($datePeriod->toArray(true)) 243 | ->isEqualTo($datePeriodFromArray) 244 | ->if($datePeriod = TestedDatePeriod::createForMonth($from, $to, TestedDatePeriod::CREATE_TO)) 245 | ->array($datePeriod->toArray(true)) 246 | ->isEqualTo($datePeriodToArray) 247 | ; 248 | 249 | $from = DateTime::createFromFormat('Y-m-d H:i:s', '2010-08-20 15:30:45'); 250 | $to = DateTime::createFromFormat('Y-m-d H:i:s', '2012-11-10 10:30:45'); 251 | 252 | $datePeriodFromArray = array( 253 | '2010-09-01 00:00:00', 254 | '2010-10-01 00:00:00', 255 | '2010-11-01 00:00:00', 256 | '2010-12-01 00:00:00', 257 | ); 258 | 259 | $datePeriodToArray = array( 260 | '2012-01-01 00:00:00', 261 | '2012-02-01 00:00:00', 262 | '2012-03-01 00:00:00', 263 | '2012-04-01 00:00:00', 264 | '2012-05-01 00:00:00', 265 | '2012-06-01 00:00:00', 266 | '2012-07-01 00:00:00', 267 | '2012-08-01 00:00:00', 268 | '2012-09-01 00:00:00', 269 | '2012-10-01 00:00:00', 270 | ); 271 | 272 | $this 273 | ->if($datePeriod = TestedDatePeriod::createForMonth($from, $to, TestedDatePeriod::CREATE_FROM)) 274 | ->array($datePeriod->toArray(true)) 275 | ->isEqualTo($datePeriodFromArray) 276 | ->if($datePeriod = TestedDatePeriod::createForMonth($from, $to, TestedDatePeriod::CREATE_TO)) 277 | ->array($datePeriod->toArray(true)) 278 | ->isEqualTo($datePeriodToArray) 279 | ; 280 | } 281 | 282 | public function testCreateForYear() 283 | { 284 | $from = DateTime::createFromFormat('Y-m-d H:i:s', '2012-11-13 10:30:45'); 285 | $to = DateTime::createFromFormat('Y-m-d H:i:s', '2012-11-13 15:30:45'); 286 | 287 | $datePeriodArray = array(); 288 | 289 | $this 290 | ->if($datePeriod = TestedDatePeriod::createForYear($from, $to)) 291 | ->array($datePeriod->toArray(true)) 292 | ->isEqualTo($datePeriodArray) 293 | ; 294 | 295 | $from = DateTime::createFromFormat('Y-m-d H:i:s', '2012-11-10 10:30:45'); 296 | $to = DateTime::createFromFormat('Y-m-d H:i:s', '2012-11-13 15:30:45'); 297 | 298 | $datePeriodArray = array(); 299 | 300 | $this 301 | ->if($datePeriod = TestedDatePeriod::createForYear($from, $to)) 302 | ->array($datePeriod->toArray(true)) 303 | ->isEqualTo($datePeriodArray) 304 | ; 305 | 306 | $from = DateTime::createFromFormat('Y-m-d H:i:s', '2012-10-10 15:30:45'); 307 | $to = DateTime::createFromFormat('Y-m-d H:i:s', '2012-11-15 10:30:45'); 308 | 309 | $datePeriodArray = array(); 310 | 311 | $this 312 | ->if($datePeriod = TestedDatePeriod::createForYear($from, $to)) 313 | ->array($datePeriod->toArray(true)) 314 | ->isEqualTo($datePeriodArray) 315 | ; 316 | 317 | $from = DateTime::createFromFormat('Y-m-d H:i:s', '2012-09-10 15:30:45'); 318 | $to = DateTime::createFromFormat('Y-m-d H:i:s', '2012-11-15 10:30:45'); 319 | 320 | $datePeriodArray = array(); 321 | 322 | $this 323 | ->if($datePeriod = TestedDatePeriod::createForYear($from, $to)) 324 | ->array($datePeriod->toArray(true)) 325 | ->isEqualTo($datePeriodArray) 326 | ; 327 | 328 | $from = DateTime::createFromFormat('Y-m-d H:i:s', '2012-08-20 15:30:45'); 329 | $to = DateTime::createFromFormat('Y-m-d H:i:s', '2012-11-10 10:30:45'); 330 | 331 | $datePeriodArray = array(); 332 | 333 | $this 334 | ->if($datePeriod = TestedDatePeriod::createForYear($from, $to)) 335 | ->array($datePeriod->toArray(true)) 336 | ->isEqualTo($datePeriodArray) 337 | ; 338 | 339 | $from = DateTime::createFromFormat('Y-m-d H:i:s', '2011-08-20 15:30:45'); 340 | $to = DateTime::createFromFormat('Y-m-d H:i:s', '2012-11-10 10:30:45'); 341 | 342 | $datePeriodArray = array(); 343 | 344 | $this 345 | ->if($datePeriod = TestedDatePeriod::createForYear($from, $to)) 346 | ->array($datePeriod->toArray(true)) 347 | ->isEqualTo($datePeriodArray) 348 | ; 349 | 350 | $from = DateTime::createFromFormat('Y-m-d H:i:s', '2010-08-20 15:30:45'); 351 | $to = DateTime::createFromFormat('Y-m-d H:i:s', '2012-11-10 10:30:45'); 352 | 353 | $datePeriodArray = array( 354 | '2011-01-01 00:00:00', 355 | ); 356 | 357 | $this 358 | ->if($datePeriod = TestedDatePeriod::createForYear($from, $to)) 359 | ->array($datePeriod->toArray(true)) 360 | ->isEqualTo($datePeriodArray) 361 | ; 362 | 363 | $from = DateTime::createFromFormat('Y-m-d H:i:s', '2009-08-20 15:30:45'); 364 | $to = DateTime::createFromFormat('Y-m-d H:i:s', '2012-11-10 10:30:45'); 365 | 366 | $datePeriodArray = array( 367 | '2010-01-01 00:00:00', 368 | '2011-01-01 00:00:00', 369 | ); 370 | 371 | $this 372 | ->if($datePeriod = TestedDatePeriod::createForYear($from, $to)) 373 | ->array($datePeriod->toArray(true)) 374 | ->isEqualTo($datePeriodArray) 375 | ; 376 | } 377 | } 378 | -------------------------------------------------------------------------------- /tests/units/UnitOfTime/AbstractUnitOfTime.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | class AbstractUnitOfTime extends atoum\test 15 | { 16 | public function testConstruct() 17 | { 18 | $dateTime = DateTime::createFromFormat('Y-m-d H:i:s', '2012-11-06 15:30:45'); 19 | 20 | $event = new \mock\FreeAgent\Bitter\UnitOfTime\AbstractUnitOfTime('drink_a_bitter_beer', $dateTime); 21 | 22 | $this 23 | ->variable($event->getDateTime()) 24 | ->isIdenticalTo($dateTime) 25 | ; 26 | $this 27 | ->variable($event->getUnitOfTimeName()) 28 | ->isIdenticalTo('drink_a_bitter_beer') 29 | ; 30 | } 31 | 32 | public function testGetKey() 33 | { 34 | $dateTime = DateTime::createFromFormat('Y-m-d H:i:s', '2012-11-06 15:30:45'); 35 | 36 | $event = new \mock\FreeAgent\Bitter\UnitOfTime\AbstractUnitOfTime('drink_a_bitter_beer', $dateTime); 37 | 38 | $event->getMockController()->getDateTimeFormated = '2012-11-06'; 39 | 40 | $this 41 | ->string($event->getKey()) 42 | ->isEqualTo('drink_a_bitter_beer:2012-11-06') 43 | ; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /tests/units/UnitOfTime/Day.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | class Day extends atoum\test 15 | { 16 | public function testConstruct() 17 | { 18 | $day = new TestedDay('drink_a_bitter_beer', new DateTime()); 19 | 20 | $this 21 | ->object($day) 22 | ->isInstanceOf('FreeAgent\Bitter\UnitOfTime\AbstractUnitOfTime') 23 | ->isInstanceOf('FreeAgent\Bitter\UnitOfTime\UnitOfTimeInterface') 24 | ; 25 | } 26 | 27 | public function testGetDateTimeFormated() 28 | { 29 | $dateTime = DateTime::createFromFormat('Y-m-d H:i:s', '2012-11-06 15:30:45'); 30 | 31 | $day = new TestedDay('drink_a_bitter_beer', $dateTime); 32 | 33 | $this 34 | ->string($day->getDateTimeFormated()) 35 | ->isEqualTo('2012-11-06') 36 | ; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /tests/units/UnitOfTime/Hour.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | class Hour extends atoum\test 15 | { 16 | public function testConstruct() 17 | { 18 | $hour = new TestedHour('drink_a_bitter_beer', new DateTime()); 19 | 20 | $this 21 | ->object($hour) 22 | ->isInstanceOf('FreeAgent\Bitter\UnitOfTime\AbstractUnitOfTime') 23 | ->isInstanceOf('FreeAgent\Bitter\UnitOfTime\UnitOfTimeInterface') 24 | ; 25 | } 26 | 27 | public function testGetDateTimeFormated() 28 | { 29 | $dateTime = DateTime::createFromFormat('Y-m-d H:i:s', '2012-11-06 15:30:45'); 30 | 31 | $hour = new TestedHour('drink_a_bitter_beer', $dateTime); 32 | 33 | $this 34 | ->string($hour->getDateTimeFormated()) 35 | ->isEqualTo('2012-11-06-15') 36 | ; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /tests/units/UnitOfTime/Month.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | class Month extends atoum\test 15 | { 16 | public function testConstruct() 17 | { 18 | $month = new TestedMonth('drink_a_bitter_beer', new DateTime()); 19 | 20 | $this 21 | ->object($month) 22 | ->isInstanceOf('FreeAgent\Bitter\UnitOfTime\AbstractUnitOfTime') 23 | ->isInstanceOf('FreeAgent\Bitter\UnitOfTime\UnitOfTimeInterface') 24 | ; 25 | } 26 | 27 | public function testGetDateTimeFormated() 28 | { 29 | $dateTime = DateTime::createFromFormat('Y-m-d H:i:s', '2012-11-06 15:30:45'); 30 | 31 | $month = new TestedMonth('drink_a_bitter_beer', $dateTime); 32 | 33 | $this 34 | ->string($month->getDateTimeFormated()) 35 | ->isEqualTo('2012-11') 36 | ; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /tests/units/UnitOfTime/Week.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | class Week extends atoum\test 15 | { 16 | public function testConstruct() 17 | { 18 | $week = new TestedWeek('drink_a_bitter_beer', new DateTime()); 19 | 20 | $this 21 | ->object($week) 22 | ->isInstanceOf('FreeAgent\Bitter\UnitOfTime\AbstractUnitOfTime') 23 | ->isInstanceOf('FreeAgent\Bitter\UnitOfTime\UnitOfTimeInterface') 24 | ; 25 | } 26 | 27 | public function testGetDateTimeFormated() 28 | { 29 | $dateTime = DateTime::createFromFormat('Y-m-d H:i:s', '2012-11-06 15:30:45'); 30 | 31 | $week = new TestedWeek('drink_a_bitter_beer', $dateTime); 32 | 33 | $this 34 | ->string($week->getDateTimeFormated()) 35 | ->isEqualTo('2012-W45') 36 | ; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /tests/units/UnitOfTime/Year.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | class Year extends atoum\test 15 | { 16 | public function testConstruct() 17 | { 18 | $month = new TestedYear('drink_a_bitter_beer', new DateTime()); 19 | 20 | $this 21 | ->object($month) 22 | ->isInstanceOf('FreeAgent\Bitter\UnitOfTime\AbstractUnitOfTime') 23 | ->isInstanceOf('FreeAgent\Bitter\UnitOfTime\UnitOfTimeInterface') 24 | ; 25 | } 26 | 27 | public function testGetDateTimeFormated() 28 | { 29 | $dateTime = DateTime::createFromFormat('Y-m-d H:i:s', '2012-11-06 15:30:45'); 30 | 31 | $month = new TestedYear('drink_a_bitter_beer', $dateTime); 32 | 33 | $this 34 | ->string($month->getDateTimeFormated()) 35 | ->isEqualTo('2012') 36 | ; 37 | } 38 | } 39 | --------------------------------------------------------------------------------