├── images ├── queued │ └── empty ├── watermarked │ └── empty └── placeholder.png ├── .gitattributes ├── fonts └── built_titling_rg.ttf ├── .gitignore ├── images.php ├── composer.json ├── config.php ├── worker.php ├── readme.md ├── src ├── Message.php └── Queue.php ├── index.php └── tutorial.md /images/queued/empty: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /images/watermarked/empty: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /images/placeholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webbgeorge/php-sqs-tutorial/HEAD/images/placeholder.png -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | * text eol=lf 3 | 4 | *.png binary 5 | *.jpg binary 6 | *.gif binary 7 | *.ttf binary -------------------------------------------------------------------------------- /fonts/built_titling_rg.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webbgeorge/php-sqs-tutorial/HEAD/fonts/built_titling_rg.ttf -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Project specific files # 2 | ###################### 3 | /images/queued/* 4 | !/images/queued/empty 5 | /images/watermarked/* 6 | !/images/watermarked/empty 7 | vendor/ 8 | 9 | # IDE and editor specific files # 10 | ################################# 11 | .idea 12 | 13 | # OS generated files # 14 | ###################### 15 | .DS_Store 16 | .DS_Store? 17 | ._* 18 | .Spotlight-V100 19 | .Trashes 20 | Icon? 21 | ehthumbs.db 22 | Thumbs.db -------------------------------------------------------------------------------- /images.php: -------------------------------------------------------------------------------- 1 | 9 | * @license http://opensource.org/licenses/MIT MIT License 10 | * @link http://george.webb.uno/posts/aws-simple-queue-service-php-sdk 11 | */ 12 | 13 | $waiting_images = scandir(__DIR__ . '/images/queued'); 14 | $watermarked_images = scandir(__DIR__ . '/images/watermarked'); 15 | 16 | $output = array('waiting' => $waiting_images, 'watermarked' => $watermarked_images); 17 | 18 | echo json_encode($output); 19 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "gaw508/php-sqs-tutorial", 3 | "version" : "0.1.0", 4 | "description" : "Example for my AWS SQS tutorial", 5 | "license" : "MIT", 6 | "authors" : [ 7 | { 8 | "name" : "George Webb", 9 | "email" : "george@webb.uno", 10 | "homepage" : "http://george.webb.uno/", 11 | "role" : "Developer" 12 | } 13 | ], 14 | "require" : { 15 | "php" : ">=5.3.0", 16 | "aws/aws-sdk-php" : "3.*" 17 | }, 18 | "require-dev": { 19 | "squizlabs/php_codesniffer": "*" 20 | }, 21 | "autoload" : { 22 | "psr-4" : { 23 | "Gaw508\\PhpSqsTutorial\\" : "src" 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /config.php: -------------------------------------------------------------------------------- 1 | 7 | * @license http://opensource.org/licenses/MIT MIT License 8 | * @link http://george.webb.uno/posts/aws-simple-queue-service-php-sdk 9 | */ 10 | 11 | /** 12 | * The name of the SQS queue 13 | */ 14 | define('QUEUE_NAME', 'watermarker'); 15 | 16 | /** 17 | * AWS Credentials array for accessing the API 18 | * 19 | * It is a serialised array, which is then unserialised when used. 20 | */ 21 | define('AWS_CREDENTIALS', serialize(array( 22 | 'region' => "[[AWS_REGION]]", 23 | 'version' => "[[AWS_VERSION]]", 24 | 'credentials' => array( 25 | 'key' => "[[AWS_ACCESS_KEY_ID]]", 26 | 'secret' => "[[AWS_SECRET_ACCESS_KEY]]" 27 | ) 28 | ))); 29 | -------------------------------------------------------------------------------- /worker.php: -------------------------------------------------------------------------------- 1 | 12 | * @license http://opensource.org/licenses/MIT MIT License 13 | * @link http://george.webb.uno/posts/aws-simple-queue-service-php-sdk 14 | */ 15 | 16 | require_once __DIR__ . '/config.php'; 17 | require_once __DIR__ . '/vendor/autoload.php'; 18 | 19 | use Gaw508\PhpSqsTutorial\Queue; 20 | 21 | // Instantiate queue with aws credentials from config. 22 | $queue = new Queue(QUEUE_NAME, unserialize(AWS_CREDENTIALS)); 23 | 24 | // Continuously poll queue for new messages and process them. 25 | while (true) { 26 | $message = $queue->receive(); 27 | if ($message) { 28 | try { 29 | $message->process(); 30 | $queue->delete($message); 31 | } catch (Exception $e) { 32 | $queue->release($message); 33 | echo $e->getMessage(); 34 | } 35 | } else { 36 | // Wait 20 seconds if no jobs in queue to minimise requests to AWS API 37 | sleep(20); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | PHP SQS Tutorial Demo 2 | ===================== 3 | 4 | For the article please visit: [http://george.webb.uno/posts/aws-simple-queue-service-php-sdk](http://george.webb.uno/posts/aws-simple-queue-service-php-sdk) 5 | 6 | There is a live demo version of this running at: [http://sqs-demo.george.webb.uno/](http://sqs-demo.george.webb.uno/) 7 | 8 | To install this demo for your self you will need a PHP web server running PHP 5.3 or later, composer, as well as PHP CLI with access to the imagick extension. 9 | 10 | ### Instructions: ### 11 | 12 | - Clone this git repo 13 | - Run composer install 14 | - Setup config.php with AWS credentials and SQS queue name 15 | - Run the worker with command: "php -d extension=imagick.so worker.php" 16 | - Visit index.php and upload some images. 17 | 18 | ### Quick explanation ### 19 | 20 | For more detail please see my tutorial article linked above, or the tutorial.md file in this repository. 21 | 22 | Essentially this is a demo showing a possible usage of SQS within a PHP application and an example implementation of the PHP AWS SDK. The demo involves a web page where a user can upload up to 10 images, which are put into a SQS queue to be watermarked. A separate component of the application is running in the background and watermarks each of these images from the queue. In reality the queue worker component would be running on a/many different servers and the image files would be stored remotely, rather than locally; however, for the sake of this demo, it is all done on the same machine. 23 | 24 | Here is a quick breakdown of the important parts of this application: 25 | 26 | - src/Queue.php is the class containing all the logic for communicating with SQS 27 | - src/Message.php is the class containing the logic involved with the messages themselves and the processing of them 28 | - index.php is the webpage which images are uploaded to and the results displayed 29 | - images.php is the API call used by the demo JS for displaying queued and watermarked images 30 | - worker.php is the queue worker which is run through the PHP CLI and processes the watermarking messages 31 | -------------------------------------------------------------------------------- /src/Message.php: -------------------------------------------------------------------------------- 1 | 9 | * @license http://opensource.org/licenses/MIT MIT License 10 | * @link http://george.webb.uno/posts/aws-simple-queue-service-php-sdk 11 | */ 12 | 13 | namespace Gaw508\PhpSqsTutorial; 14 | 15 | use Imagick; 16 | use ImagickDraw; 17 | 18 | class Message 19 | { 20 | /** 21 | * The path of the uploaded file to be processed 22 | * 23 | * @var string 24 | */ 25 | public $input_file_path; 26 | 27 | /** 28 | * The path to output the processed file 29 | * 30 | * @var string 31 | */ 32 | public $output_file_path; 33 | 34 | /** 35 | * The receipt handle from SQS, used to identify the message when interacting with the queue 36 | * 37 | * @var string 38 | */ 39 | public $receipt_handle; 40 | 41 | /** 42 | * Construct the object with message data and optional receipt_handle if relevant 43 | * 44 | * @param string|array $data JSON String or an assoc array containing the message data 45 | * @param string $receipt_handle The sqs receipt handle of the message 46 | */ 47 | public function __construct($data, $receipt_handle = '') 48 | { 49 | // If data is a json string, decode it into an assoc array 50 | if (is_string($data)) { 51 | $data = json_decode($data, true); 52 | } 53 | 54 | // Assign the data values and receipt handle to the object 55 | $this->input_file_path = $data['input_file_path']; 56 | $this->output_file_path = $data['output_file_path']; 57 | $this->receipt_handle = $receipt_handle; 58 | } 59 | 60 | /** 61 | * Returns the data of the message as a JSON string 62 | * 63 | * @return string JSON message data 64 | */ 65 | public function asJson() 66 | { 67 | return json_encode(array( 68 | 'input_file_path' => $this->input_file_path, 69 | 'output_file_path' => $this->output_file_path 70 | )); 71 | } 72 | 73 | /** 74 | * Processes an image given in the input file path, and outputs it in the output file path 75 | * 76 | * Takes the input image, creates a 300x300px thumbnail and overlays a text watermark. 77 | * Then deletes the input image. 78 | */ 79 | public function process() 80 | { 81 | // Crete Imagick object from input image 82 | $image = new Imagick($this->input_file_path); 83 | 84 | // Crops the image into a 300x300px thumbnail 85 | $image->cropthumbnailimage(300, 300); 86 | 87 | // Set the watermark text 88 | $text = 'WATERMARK!!!'; 89 | 90 | // Create a new drawing palette 91 | $draw = new ImagickDraw(); 92 | 93 | // Set font properties 94 | $draw->setFont(__DIR__ . '/../fonts/built_titling_rg.ttf'); 95 | $draw->setFontSize(26); 96 | $draw->setFillColor('black'); 97 | $draw->setGravity(Imagick::GRAVITY_CENTER); 98 | 99 | // Draw the watermark onto the image 100 | $image->annotateImage($draw, 10, 12, 0, $text); 101 | $draw->setFillColor('white'); 102 | $image->annotateImage($draw, 11, 11, 0, $text); 103 | 104 | // Set output image format 105 | $image->setImageFormat('jpg'); 106 | 107 | // Output the processed image to the output path (as .jpg) 108 | $output_path = explode('.', $this->output_file_path); 109 | array_pop($output_path); 110 | $output_path = implode($output_path) . '.jpg'; 111 | $image->writeImage($output_path); 112 | 113 | // Delete the input image 114 | unlink($this->input_file_path); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/Queue.php: -------------------------------------------------------------------------------- 1 | 9 | * @license http://opensource.org/licenses/MIT MIT License 10 | * @link http://george.webb.uno/posts/aws-simple-queue-service-php-sdk 11 | */ 12 | 13 | namespace Gaw508\PhpSqsTutorial; 14 | 15 | use Exception; 16 | use Aws\Sqs\SqsClient; 17 | 18 | class Queue 19 | { 20 | /** 21 | * The name of the SQS queue 22 | * 23 | * @var string 24 | */ 25 | private $name; 26 | 27 | /** 28 | * The url of the SQS queue 29 | * 30 | * @var string 31 | */ 32 | private $url; 33 | 34 | /** 35 | * The array of credentials used to connect to the AWS API 36 | * 37 | * @var array 38 | */ 39 | private $aws_credentials; 40 | 41 | /** 42 | * A SqsClient object from the AWS SDK, used to connect to the AWS SQS API 43 | * 44 | * @var SqsClient 45 | */ 46 | private $sqs_client; 47 | 48 | /** 49 | * Constructs the wrapper using the name of the queue and the aws credentials 50 | * 51 | * @param $name 52 | * @param $aws_credentials 53 | */ 54 | public function __construct($name, $aws_credentials) 55 | { 56 | try { 57 | // Setup the connection to the queue 58 | $this->name = $name; 59 | $this->aws_credentials = $aws_credentials; 60 | $this->sqs_client = new SqsClient($this->aws_credentials); 61 | 62 | // Get the queue URL 63 | $this->url = $this->sqs_client->getQueueUrl(array('QueueName' => $this->name))->get('QueueUrl'); 64 | } catch (Exception $e) { 65 | echo 'Error getting the queue url ' . $e->getMessage(); 66 | } 67 | } 68 | 69 | /** 70 | * Sends a message to SQS using a JSON output from a given Message object 71 | * 72 | * @param Message $message A message object to be sent to the queue 73 | * @return bool returns true if message is sent successfully, otherwise false 74 | */ 75 | public function send(Message $message) 76 | { 77 | try { 78 | // Send the message 79 | $this->sqs_client->sendMessage(array( 80 | 'QueueUrl' => $this->url, 81 | 'MessageBody' => $message->asJson() 82 | )); 83 | 84 | return true; 85 | } catch (Exception $e) { 86 | echo 'Error sending message to queue ' . $e->getMessage(); 87 | return false; 88 | } 89 | } 90 | 91 | /** 92 | * Receives a message from the queue and puts it into a Message object 93 | * 94 | * @return bool|Message Message object built from the queue, or false if there is a problem receiving message 95 | */ 96 | public function receive() 97 | { 98 | try { 99 | // Receive a message from the queue 100 | $result = $this->sqs_client->receiveMessage(array( 101 | 'QueueUrl' => $this->url 102 | )); 103 | 104 | if ($result['Messages'] == null) { 105 | // No message to process 106 | return false; 107 | } 108 | 109 | // Get the message and return it 110 | $result_message = array_pop($result['Messages']); 111 | return new Message($result_message['Body'], $result_message['ReceiptHandle']); 112 | } catch (Exception $e) { 113 | echo 'Error receiving message from queue ' . $e->getMessage(); 114 | return false; 115 | } 116 | } 117 | 118 | /** 119 | * Deletes a message from the queue 120 | * 121 | * @param Message $message 122 | * @return bool returns true if successful, false otherwise 123 | */ 124 | public function delete(Message $message) 125 | { 126 | try { 127 | // Delete the message 128 | $this->sqs_client->deleteMessage(array( 129 | 'QueueUrl' => $this->url, 130 | 'ReceiptHandle' => $message->receipt_handle 131 | )); 132 | 133 | return true; 134 | } catch (Exception $e) { 135 | echo 'Error deleting message from queue ' . $e->getMessage(); 136 | return false; 137 | } 138 | } 139 | 140 | /** 141 | * Releases a message back to the queue, making it visible again 142 | * 143 | * @param Message $message 144 | * @return bool returns true if successful, false otherwise 145 | */ 146 | public function release(Message $message) 147 | { 148 | try { 149 | // Set the visibility timeout to 0 to make the message visible in the queue again straight away 150 | $this->sqs_client->changeMessageVisibility(array( 151 | 'QueueUrl' => $this->url, 152 | 'ReceiptHandle' => $message->receipt_handle, 153 | 'VisibilityTimeout' => 0 154 | )); 155 | 156 | return true; 157 | } catch (Exception $e) { 158 | echo 'Error releasing job back to queue ' . $e->getMessage(); 159 | return false; 160 | } 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | 8 | * @license http://opensource.org/licenses/MIT MIT License 9 | * @link http://george.webb.uno/posts/aws-simple-queue-service-php-sdk 10 | */ 11 | 12 | require_once __DIR__ . '/config.php'; 13 | require_once __DIR__ . '/vendor/autoload.php'; 14 | 15 | use Gaw508\PhpSqsTutorial\Message; 16 | use Gaw508\PhpSqsTutorial\Queue; 17 | 18 | // Array of messages to be displayed to the user. 19 | $warnings = array(); 20 | 21 | if ( !empty($_FILES)) { 22 | // check number of files to upload 23 | $number_of_images = count($_FILES['images']['name']); 24 | 25 | // Only upload a max of 10 files 26 | if ($number_of_images > 10) { 27 | $warnings[] = array( 28 | 'class' => 'alert-danger', 29 | 'text' => 'Too many images, please upload a maximum of 10 images.' 30 | ); 31 | } else { 32 | $successes = 0; 33 | 34 | // For each upload, check if an image and valid etc. 35 | for ($i = 0; $i < $number_of_images; $i++) { 36 | if ($_FILES['images']['error'][$i] > 0) { 37 | $warnings[] = array('class' => 'alert-danger', 'text' => 'Error uploading file.'); 38 | } elseif ( !filesize($_FILES['images']['tmp_name'][$i])) { 39 | $warnings[] = array('class' => 'alert-danger', 'text' => 'Error uploading file.'); 40 | } elseif ($_FILES['images']['type'][$i] != 'image/png' and $_FILES['images']['type'][$i] != 'image/jpeg') { 41 | $warnings[] = array('class' => 'alert-danger', 'text' => 'Invalid file type.'); 42 | } elseif ($_FILES['images']['size'][$i] > 2000000) { 43 | $warnings[] = array('class' => 'alert-danger', 'text' => 'File too big.'); 44 | } else { 45 | // Create a new filename for the uploaded image and move it there 46 | $extension = $_FILES['images']['type'][$i] == 'image/png' ? '.png' : '.jpg'; 47 | $new_name = uniqid() . $extension; 48 | if ( !move_uploaded_file($_FILES['images']['tmp_name'][$i], __DIR__ . '/images/queued/' . $new_name)) { 49 | $warnings[] = array('class' => 'alert-danger', 'text' => 'Error uploading file.'); 50 | } else { 51 | // Create a new message with processing instructions and push to SQS queue 52 | $message = new Message(array( 53 | 'input_file_path' => __DIR__ . '/images/queued/' . $new_name, 54 | 'output_file_path' => __DIR__ . '/images/watermarked/' . $new_name 55 | )); 56 | $queue = new Queue(QUEUE_NAME, unserialize(AWS_CREDENTIALS)); 57 | if ($queue->send($message)) { 58 | $successes++; 59 | } else { 60 | $warnings[] = array('class' => 'alert-danger', 'text' => 'Error adding file to queue.'); 61 | } 62 | } 63 | } 64 | } 65 | 66 | if ($successes > 0) { 67 | $warnings[] = array('class' => 'alert-success', 'text' => "$successes images uploaded successfully."); 68 | $warnings[] = array('class' => 'alert-info', 'text' => "Uploaded images added to queue..."); 69 | } 70 | } 71 | } 72 | 73 | ?> 74 | 75 | 76 |
77 |