First round of updates finished.

- Needs better test cases.
This commit is contained in:
2015-09-30 15:12:35 -05:00
parent 49677e8bf2
commit f03508bf3e
4 changed files with 207 additions and 70 deletions

View File

@@ -1,7 +1,7 @@
ugly-queue ugly-queue
========== ==========
A simple file-based queue system for PHP 5.3.3+ A simple file-based FIFO queue system for PHP 5.3.3+
Build statuses: Build statuses:
- master: [![Build Status](https://travis-ci.org/dcarbone/ugly-queue.svg?branch=master)](https://travis-ci.org/dcarbone/ugly-queue) - master: [![Build Status](https://travis-ci.org/dcarbone/ugly-queue.svg?branch=master)](https://travis-ci.org/dcarbone/ugly-queue)
@@ -20,15 +20,74 @@ Once installed, you must first initialize an instance of [src/UglyQueueManager.p
This is done as such: This is done as such:
```php ```php
$config = array( $queueBaseDir = 'path to where you would like queue files and directories to be stored';
'queue-base-dir' => 'path to where you would like queue files and directories to be stored'
);
$manager = new UglyQueueManager($config); $manager = new UglyQueueManager($queueBaseDir);
``` ```
Once initialized, you can start adding queues! Once initialized, you can start adding queues!
```php ```php
$manager $sandwichQueue = $manager->createQueue('sandwiches');
```
$sandwichQueue->lock();
$sandwichQueue->addItems(array(
'bread',
'meat',
'cheese',
'lettuce',
'bread'
));
$sandwichQueue->unlock();
```
Once you have items added to the queue, you can either pull items out ad-hoc or set up some sort of cron
or schedule task to process items regularly.
If the base directory for all of your queues remains the same, each initialization
of `UglyQueueManager` will automatically find and initialize instances of pre-existing
UglyQueues.
In a subsequent request, simply do the following:
```php
$queueBaseDir = 'path to where you would like queue files and directories to be stored';
$manager = new UglyQueueManager($queueBaseDir);
// 'tasty-sandwich' queue will exist now
$tastySandwich = $manager->getQueue('sandwiches');
$tastySandwich->lock();
// Specify the number of items you wish to retrieve from the queue
$items = $tastySandwich->retrieveItems(4);
// $items is now an array...
var_export($items);
/*
array (
4 => 'bread',
3 => 'lettuce',
2 => 'cheese',
1 => 'meat',
)
*/
```
The queue will then retain a single item, `0 => 'bread'` as the 5th item left in the queue.
At any time you can determine how many items remain in a queue by executing `count($queueObj);`
There are a few limitations currently:
1. This lib is designed for small values without much in the way of formatting or line breaks
2. It is designed to be atomic, meaning that only one process can be adding / retrieving items from
a queue at a time. Reading actions (count, searching, etc) are NOT atomic, however.

View File

@@ -68,39 +68,7 @@ class UglyQueue implements \Serializable, \SplSubject, \Countable
$this->queueTmpFile = sprintf('%s%squeue.tmp', $path, DIRECTORY_SEPARATOR); $this->queueTmpFile = sprintf('%s%squeue.tmp', $path, DIRECTORY_SEPARATOR);
$this->serializeFile = sprintf('%s%sugly-queue.obj', $path, DIRECTORY_SEPARATOR); $this->serializeFile = sprintf('%s%sugly-queue.obj', $path, DIRECTORY_SEPARATOR);
if (is_readable($this->path) && is_writable($this->path)) $this->initialize();
$this->mode = self::QUEUE_READWRITE;
else if (is_readable($this->path))
$this->mode = self::QUEUE_READONLY;
if (!file_exists($this->path.'/index.html'))
{
if ($this->mode === self::QUEUE_READONLY)
throw new \RuntimeException('Cannot initialize queue with name "'.$this->name.'", the user lacks permission to create files.');
$html = <<<HTML
<html>
<head>
<title>403 Forbidden</title>
</head>
<body>
<p>Directory access is forbidden.</p>
</body>
</html>
HTML;
file_put_contents($this->path.'/index.html', $html);
}
if (!file_exists($this->queueFile))
{
if ($this->mode === self::QUEUE_READONLY)
throw new \RuntimeException('Cannot initialize queue with name "'.$this->name.'", the user lacks permission to create files.');
file_put_contents($this->queueFile, '');
}
$this->_notifyStatus = UglyQueueEnum::QUEUE_INITIALIZED;
$this->notify();
} }
/** /**
@@ -177,6 +145,14 @@ HTML;
return $this->serializeFile; return $this->serializeFile;
} }
/**
* @return boolean
*/
public function isLocked()
{
return $this->locked;
}
/** /**
* @param int $ttl Time to live in seconds * @param int $ttl Time to live in seconds
* @throws \InvalidArgumentException * @throws \InvalidArgumentException
@@ -190,10 +166,8 @@ HTML;
if ($ttl < 1) if ($ttl < 1)
throw new \InvalidArgumentException('Argument 1 expected to be greater than 0 "'.$ttl.'" seen'); throw new \InvalidArgumentException('Argument 1 expected to be greater than 0 "'.$ttl.'" seen');
$alreadyLocked = $this->isLocked();
// If there is currently no lock // If there is currently no lock
if ($alreadyLocked === false) if ($this->isAlreadyLocked() === false)
return $this->createLockFile($ttl); return $this->createLockFile($ttl);
// If we make it this far, there is already a lock in place. // If we make it this far, there is already a lock in place.
@@ -209,7 +183,7 @@ HTML;
*/ */
public function unlock() public function unlock()
{ {
if ($this->locked === true) if ($this->isLocked() === true)
{ {
unlink($this->lockFile); unlink($this->lockFile);
$this->locked = false; $this->locked = false;
@@ -223,7 +197,7 @@ HTML;
* @throws \RuntimeException * @throws \RuntimeException
* @return bool * @return bool
*/ */
public function isLocked() public function isAlreadyLocked()
{ {
// First check for lock file // First check for lock file
if (is_file($this->lockFile)) if (is_file($this->lockFile))
@@ -300,7 +274,7 @@ HTML;
if ($i++ >= $start_line) if ($i++ >= $start_line)
{ {
list ($key, $value) = $line; list ($key, $value) = $line;
$data[$key] = $value; $data = array($key => $value) + $data;
} }
} }
@@ -349,7 +323,7 @@ HTML;
throw new \RuntimeException('Cannot add item to queue "'.$this->name.'" as it is in read-only mode'); throw new \RuntimeException('Cannot add item to queue "'.$this->name.'" as it is in read-only mode');
// If we don't have a lock, assume issue and move on. // If we don't have a lock, assume issue and move on.
if ($this->locked === false) if ($this->isLocked() === false)
throw new \RuntimeException('Cannot add item to queue "'.$this->name.'". Queue is already locked by another process'); throw new \RuntimeException('Cannot add item to queue "'.$this->name.'". Queue is already locked by another process');
if (!is_resource($this->tmpHandle)) if (!is_resource($this->tmpHandle))
@@ -368,6 +342,17 @@ HTML;
."\n"); ."\n");
} }
/**
* @param array $items
*/
public function addItems(array $items)
{
foreach($items as $k=>$v)
{
$this->addItem($k, $v);
}
}
/** /**
* If there is a tmp queue file, add it's contents to the beginning of a new queue file * If there is a tmp queue file, add it's contents to the beginning of a new queue file
* *
@@ -441,7 +426,17 @@ HTML;
*/ */
public function serialize() public function serialize()
{ {
return serialize(array($this->name, $this->path)); return serialize(
array(
$this->baseDir,
$this->name,
$this->path,
$this->queueFile,
$this->queueTmpFile,
$this->lockFile,
$this->serializeFile,
)
);
} }
/** /**
@@ -454,10 +449,15 @@ HTML;
*/ */
public function unserialize($serialized) public function unserialize($serialized)
{ {
/** @var \DCarbone\UglyQueue $uglyQueue */
$data = unserialize($serialized); $data = unserialize($serialized);
$this->name = $data[0]; $this->baseDir = $data[0];
$this->path = $data[1]; $this->name = $data[1];
$this->path = $data[2];
$this->queueFile = $data[3];
$this->queueTmpFile = $data[4];
$this->lockFile = $data[5];
$this->serializeFile = $data[6];
$this->initialize();
} }
/** /**
@@ -504,6 +504,26 @@ HTML;
} }
} }
/**
* This method is mostly intended to check the "validity" of a re-initialized queue
*
* Could probably stand to be improved.
*
* @return bool
*/
public function _valid()
{
return (
$this->baseDir !== null &&
$this->name !== null &&
$this->path !== null &&
$this->queueFile !== null &&
$this->queueTmpFile !== null &&
$this->lockFile !== null &&
$this->serializeFile !== null
);
}
// -------- // --------
/** /**
@@ -524,10 +544,51 @@ HTML;
} }
$this->locked = true; $this->locked = true;
$this->_notifyStatus = UglyQueueEnum::QUEUE_LOCKED; $this->_notifyStatus = UglyQueueEnum::QUEUE_LOCKED;
$this->notify(); $this->notify();
return true; return true;
} }
/**
* Post-construct initialization method.
*
* Also used post-un-serialization
*/
protected function initialize()
{
if (is_readable($this->path) && is_writable($this->path))
$this->mode = self::QUEUE_READWRITE;
else if (is_readable($this->path))
$this->mode = self::QUEUE_READONLY;
if (!file_exists($this->path.'/index.html'))
{
if ($this->mode === self::QUEUE_READONLY)
throw new \RuntimeException('Cannot initialize queue with name "'.$this->name.'", the user lacks permission to create files.');
$html = <<<HTML
<html>
<head>
<title>403 Forbidden</title>
</head>
<body>
<p>Directory access is forbidden.</p>
</body>
</html>
HTML;
file_put_contents($this->path.'/index.html', $html);
}
if (!file_exists($this->queueFile))
{
if ($this->mode === self::QUEUE_READONLY)
throw new \RuntimeException('Cannot initialize queue with name "'.$this->name.'", the user lacks permission to create files.');
file_put_contents($this->queueFile, '');
}
$this->_notifyStatus = UglyQueueEnum::QUEUE_INITIALIZED;
$this->notify();
}
} }

View File

@@ -38,6 +38,22 @@ class UglyQueueManager implements \SplObserver, \Countable
} }
} }
/**
* @param string $name
* @return UglyQueue|UglyQueueManager
*/
public function getQueue($name)
{
if (isset($this->queues[$name]))
return $this->queues[$name];
$path = sprintf('%s/%s', $this->baseDir, $name);
if (file_exists($path))
return $this->addQueueAtPath($path);
return $this->createQueue($name);
}
/** /**
* @param UglyQueue $uglyQueue * @param UglyQueue $uglyQueue
* @return \DCarbone\UglyQueueManager * @return \DCarbone\UglyQueueManager
@@ -61,7 +77,8 @@ class UglyQueueManager implements \SplObserver, \Countable
*/ */
public function createQueue($name) public function createQueue($name)
{ {
$this->addQueue(new UglyQueue($this->baseDir, $name, array($this))); $queue = new UglyQueue($this->baseDir, $name, array($this));
$this->addQueue($queue);
return end($this->queues); return end($this->queues);
} }
@@ -80,11 +97,15 @@ class UglyQueueManager implements \SplObserver, \Countable
return null; return null;
$serializedFile = sprintf('%s/%s/ugly-queue.obj', $this->baseDir, $queueName); $serializedFile = sprintf('%s/%s/ugly-queue.obj', $this->baseDir, $queueName);
/** @var \DCarbone\UglyQueue $uglyQueue */
if (file_exists($serializedFile)) if (file_exists($serializedFile))
$uglyQueue = unserialize(file_get_contents($serializedFile)); $uglyQueue = unserialize(file_get_contents($serializedFile));
else
if (!isset($uglyQueue) || $uglyQueue->_valid() === false)
$uglyQueue = new UglyQueue($this->baseDir, $queueName, array($this)); $uglyQueue = new UglyQueue($this->baseDir, $queueName, array($this));
$uglyQueue->attach($this);
return $this->addQueue($uglyQueue); return $this->addQueue($uglyQueue);
} }

View File

@@ -209,14 +209,14 @@ class UglyQueueTest extends PHPUnit_Framework_TestCase
} }
/** /**
* @covers \DCarbone\UglyQueue::isLocked * @covers \DCarbone\isAlreadyLocked::isAlreadyLocked
* @uses \DCarbone\UglyQueue * @uses \DCarbone\UglyQueue
* @depends testCanInitializeObjectWithValidParameters * @depends testCanInitializeObjectWithValidParameters
* @param \DCarbone\UglyQueue $uglyQueue * @param \DCarbone\UglyQueue $uglyQueue
*/ */
public function testCanGetQueueLockedStatus(\DCarbone\UglyQueue $uglyQueue) public function testCanGetQueueLockedStatus(\DCarbone\UglyQueue $uglyQueue)
{ {
$locked = $uglyQueue->isLocked(); $locked = $uglyQueue->isAlreadyLocked();
$this->assertFalse($locked); $this->assertFalse($locked);
} }
@@ -239,7 +239,6 @@ class UglyQueueTest extends PHPUnit_Framework_TestCase
*/ */
public function testCanInitializeExistingQueue() public function testCanInitializeExistingQueue()
{ {
$uglyQueue = new \DCarbone\UglyQueue($this->baseDir, 'tasty-sandwich'); $uglyQueue = new \DCarbone\UglyQueue($this->baseDir, 'tasty-sandwich');
$this->assertInstanceOf('\\DCarbone\\UglyQueue', $uglyQueue); $this->assertInstanceOf('\\DCarbone\\UglyQueue', $uglyQueue);
@@ -256,7 +255,6 @@ class UglyQueueTest extends PHPUnit_Framework_TestCase
*/ */
public function testExceptionThrownWhenPassingNonIntegerValueToLock(\DCarbone\UglyQueue $uglyQueue) public function testExceptionThrownWhenPassingNonIntegerValueToLock(\DCarbone\UglyQueue $uglyQueue)
{ {
$uglyQueue->lock('7 billion'); $uglyQueue->lock('7 billion');
} }
@@ -269,13 +267,12 @@ class UglyQueueTest extends PHPUnit_Framework_TestCase
*/ */
public function testExceptionThrownWhenPassingNegativeIntegerValueToLock(\DCarbone\UglyQueue $uglyQueue) public function testExceptionThrownWhenPassingNegativeIntegerValueToLock(\DCarbone\UglyQueue $uglyQueue)
{ {
$uglyQueue->lock(-73); $uglyQueue->lock(-73);
} }
/** /**
* @covers \DCarbone\UglyQueue::lock * @covers \DCarbone\UglyQueue::lock
* @covers \DCarbone\UglyQueue::isLocked * @covers \DCarbone\isAlreadyLocked::isAlreadyLocked
* @covers \DCarbone\UglyQueue::createLockFile * @covers \DCarbone\UglyQueue::createLockFile
* @uses \DCarbone\UglyQueue * @uses \DCarbone\UglyQueue
* @depends testCanInitializeObjectWithValidParameters * @depends testCanInitializeObjectWithValidParameters
@@ -284,7 +281,6 @@ class UglyQueueTest extends PHPUnit_Framework_TestCase
*/ */
public function testCanLockUglyQueueWithDefaultTTL(\DCarbone\UglyQueue $uglyQueue) public function testCanLockUglyQueueWithDefaultTTL(\DCarbone\UglyQueue $uglyQueue)
{ {
$locked = $uglyQueue->lock(); $locked = $uglyQueue->lock();
$this->assertTrue($locked); $this->assertTrue($locked);
@@ -303,7 +299,7 @@ class UglyQueueTest extends PHPUnit_Framework_TestCase
/** /**
* @covers \DCarbone\UglyQueue::lock * @covers \DCarbone\UglyQueue::lock
* @covers \DCarbone\UglyQueue::isLocked * @covers \DCarbone\isAlreadyLocked::isAlreadyLocked
* @uses \DCarbone\UglyQueue * @uses \DCarbone\UglyQueue
* @depends testCanInitializeExistingQueue * @depends testCanInitializeExistingQueue
* @param \DCarbone\UglyQueue $uglyQueue * @param \DCarbone\UglyQueue $uglyQueue
@@ -317,7 +313,7 @@ class UglyQueueTest extends PHPUnit_Framework_TestCase
} }
/** /**
* @covers \DCarbone\UglyQueue::isLocked * @covers \DCarbone\isAlreadyLocked::isAlreadyLocked
* @uses \DCarbone\UglyQueue * @uses \DCarbone\UglyQueue
* @depends testCanLockUglyQueueWithDefaultTTL * @depends testCanLockUglyQueueWithDefaultTTL
* @param \DCarbone\UglyQueue $uglyQueue * @param \DCarbone\UglyQueue $uglyQueue
@@ -325,7 +321,7 @@ class UglyQueueTest extends PHPUnit_Framework_TestCase
public function testIsLockedReturnsTrueAfterLocking(\DCarbone\UglyQueue $uglyQueue) public function testIsLockedReturnsTrueAfterLocking(\DCarbone\UglyQueue $uglyQueue)
{ {
$isLocked = $uglyQueue->isLocked(); $isLocked = $uglyQueue->isAlreadyLocked();
$this->assertTrue($isLocked); $this->assertTrue($isLocked);
} }
@@ -348,7 +344,7 @@ class UglyQueueTest extends PHPUnit_Framework_TestCase
} }
/** /**
* @covers \DCarbone\UglyQueue::isLocked * @covers \DCarbone\isAlreadyLocked::isAlreadyLocked
* @uses \DCarbone\UglyQueue * @uses \DCarbone\UglyQueue
* @depends testCanUnlockLockedQueue * @depends testCanUnlockLockedQueue
* @param \DCarbone\UglyQueue $uglyQueue * @param \DCarbone\UglyQueue $uglyQueue
@@ -356,14 +352,14 @@ class UglyQueueTest extends PHPUnit_Framework_TestCase
public function testIsLockedReturnsFalseAfterUnlockingQueue(\DCarbone\UglyQueue $uglyQueue) public function testIsLockedReturnsFalseAfterUnlockingQueue(\DCarbone\UglyQueue $uglyQueue)
{ {
$isLocked = $uglyQueue->isLocked(); $isLocked = $uglyQueue->isAlreadyLocked();
$this->assertFalse($isLocked); $this->assertFalse($isLocked);
} }
/** /**
* @covers \DCarbone\UglyQueue::lock * @covers \DCarbone\UglyQueue::lock
* @covers \DCarbone\UglyQueue::isLocked * @covers \DCarbone\isAlreadyLocked::isAlreadyLocked
* @uses \DCarbone\UglyQueue * @uses \DCarbone\UglyQueue
* @uses \DCarbone\Helpers\FileHelper * @uses \DCarbone\Helpers\FileHelper
* @depends testCanUnlockLockedQueue * @depends testCanUnlockLockedQueue
@@ -373,18 +369,18 @@ class UglyQueueTest extends PHPUnit_Framework_TestCase
{ {
$uglyQueue->lock(2); $uglyQueue->lock(2);
$isLocked = $uglyQueue->isLocked(); $isLocked = $uglyQueue->isAlreadyLocked();
$this->assertTrue($isLocked); $this->assertTrue($isLocked);
sleep(3); sleep(3);
$isLocked = $uglyQueue->isLocked(); $isLocked = $uglyQueue->isAlreadyLocked();
$this->assertFalse($isLocked); $this->assertFalse($isLocked);
} }
/** /**
* @covers \DCarbone\UglyQueue::lock * @covers \DCarbone\UglyQueue::lock
* @covers \DCarbone\UglyQueue::isLocked * @covers \DCarbone\isAlreadyLocked::isAlreadyLocked
* @uses \DCarbone\UglyQueue * @uses \DCarbone\UglyQueue
* @depends testCanUnlockLockedQueue * @depends testCanUnlockLockedQueue
* @param \DCarbone\UglyQueue $uglyQueue * @param \DCarbone\UglyQueue $uglyQueue