Using igorw/retry to handle collision of Uuid
While the Uuid collide probability is almost zero, and only happen when you have a very very very bad luck, there is always a chance for it. If you’re using PHP, I encourage to use igorw/retry! You can retry your process as many as your wish!
Let’s grab it by adding into composer require :
composer require "igorw/retry:dev-master"
And as demo, I’m going to use “rhumsaa/uuid”, we can add again in composer require :
composer require "rhumsaa/uuid:~2.8"
Great!, let's use it :
require 'vendor/autoload.php'; use function igorw\retry; use Rhumsaa\Uuid\Uuid; $uuid = Uuid::uuid4(); $uuidStr = $uuid->toString();
Now, the Uuid instance and its string random generation created, so we can use it in retry.
$i = 0; // retry maximum : 5 times until succeed retry(5, function () use ($uuid, $uuidStr, &$i) { $i++; if ($i > 1) { $uuidStr = $uuid->toString(); } // this is pseudo code $this->db('user')->insert([ 'id' => $uuidStr, 'name' => 'foo' ]); });
When your insertion failed and got exception, it will silently ( doesn’t show error message ) and do re-try again with new string generation instead and stop when it succeded. You can change how many retry by changing 1st parameter in retry function.
References :
Zend Framework 2 : Check request URI matched against registered Router
When you develop web application using Zend Framework 2, there is sometime you get the situation that you need to check requested URI matched against registered ‘Router’, for example : you have uri : http://zf2app/register?redirect=/user which the application will redirect to passed ‘redirect’ parameter. What if user make bypassed and inject unexpected
redirect
param ? For example, you have code like this in your controller action :
$redirect = $this->params()->fromQuery('redirect',''); return $this->redirect()->toUrl($redirect);
We can inject unwanted redirect into browser, and BOOM, the site will be redirected to unwanted uri. So, we need to handle it! we need to check whenever the ‘redirect’ value can be matched with registered Router
.
First, check if the request URI can be matched against registered Router.
$redirect = $this->params()->fromQuery('redirect',''); $request = $this->getRequest(); $request->setUri($redirect); // assignment, not Comparison, that's why use single '=' if ($routeToBeMatched = $this->getServiceLocator()->get('Router')->match($request)) { // ... process redirection based on 'redirect' param... }
When request URI matched against registered Router, then we assign it to $routeToBeMatched
variable, we can procees it. What if the “redirect route equals current route” ? We can check it under if.
if ($routeToBeMatched = $this->getServiceLocator()->get('Router')->match($request)) { // handle if redirect route = current route $currentRouteMatchName = $this->getEvent()->getRouteMatch()->getMatchedRouteName(); if ($routeToBeMatched->getMatchedRouteName() != $currentRouteMatchName) { return $this->redirect()->toUrl($redirect); } }
Great! now, handle default redirection when doesnt’ match :
if ($routeToBeMatched = $this->getServiceLocator()->get('Router')->match($request)) { // handle if redirect route = current route $currentRouteMatchName = $this->getEvent()->getRouteMatch()->getMatchedRouteName(); if ($routeToBeMatched->getMatchedRouteName() != $currentRouteMatchName) { return $this->redirect()->toUrl($redirect); } } return $this->redirect()->toUrl('/user');
Ok, this is the complete code sample:
$redirect = $this->params()->fromQuery('redirect',''); $request = $this->getRequest(); $request->setUri($redirect); if ($routeToBeMatched = $this->getServiceLocator()->get('Router')->match($request)) { // handle if redirect route = current route $currentRouteMatchName = $this->getEvent()->getRouteMatch()->getMatchedRouteName(); if ($routeToBeMatched->getMatchedRouteName() != $currentRouteMatchName) { return $this->redirect()->toRoute($redirect); } } return $this->redirect()->toUrl('/user');
Update: There is a module for that that I created, go grab it here: https://github.com/samsonasik/RedirectHandlerModule 😉
Symfony 2.6.1 : Using class_alias of Debug’s FlattenException to work with Error page previews
Error page previews is one of the new features that land in Symfony 2.6. You should be able to access
/_error/{statusCode}.{format}
. Unfortunatelly, when I try in Symfony 2.6.1, It doesn’t work. The reason of issue is that in TwigBundle\Controller\ExceptionController::showAction() require HttpKernel’s FlattenException that should be require Debug’s FlattenException.
The HttpKernel’s FlattenException is deprecated from Symfony 2.3 and should be removed in 3.0.
If you see the Symfony\Bundle\TwigBundle\Controller\ExceptionController
, you will see :
use Symfony\Component\HttpKernel\Exception\FlattenException; // ... // ... public function showAction( Request $request, FlattenException $exception, DebugLoggerInterface $logger = null) { } // ... // ...
So, You just cannot make custom exception that extends the ExceptionController because of the typehint requirement. What we can do ? The issue already reported in github so we just can wait to make it work in next maintenance release – OR – do tricky in the skeleton! Don’t do it in vendor libraries directly. You shouldn’t touch it anymore. We can edit our symfony-standard skeleton application in web/app_dev.php
by apply class_alias after loader loaded :
// web/app_dev.php // ... $loader = require_once __DIR__.'/../app/bootstrap.php.cache'; class_alias( 'Symfony\Component\Debug\Exception\FlattenException', 'Symfony\Component\HttpKernel\Exception\FlattenException' ); // ...
That’s it, so when you try access : http://127.0.0.1:8000/_error/404.html or whatever your Symfony url skeleton application with /_error/404.html
, you will get something like this :
Done 😉
Using PHP Phantomjs with Codeception
When you do a web acceptance test for web with javascript support, and you want to use phantomjs but doesn’t have root access to install new application, you probably need this, the “jonnyw/php-phantomjs” lib. You can install via composer parallel with “codeception/codeception” by configuring composer.json like the following :
{ "require": { "codeception/codeception": "2.*", "jonnyw/php-phantomjs": "3.*" }, "scripts": { "post-install-cmd": [ "PhantomInstaller\\Installer::installPhantomJS" ], "post-update-cmd": [ "PhantomInstaller\\Installer::installPhantomJS" ] } }
and then run :
composer install
Ok, when everything installed, you already have : vendor/bin/phantomjs
executable file.
If you add :
"config": { "bin-dir": "bin" }
Inside your composer.json, you should have individual bin folder outside the vendor folder so you can run from it like described at http://jonnnnyw.github.io/php-phantomjs/. I personally like it should be inside the vendor instead. You can choose what you prefer.
Next step is start the phantomjs service by run command :
vendor/bin/phantomjs --webdriver=4444
Let’s continue to the sample page, try create a sample html page :
<html> <body> <h1>Hello World!</h1> <button name="test-btn" id="test-btn">test-btn</button> <script type="text/javascript" src="jquery.js"></script> <script type="text/javascript"> $(document).ready(function() { $("#test-btn").click(function() { $("h1").html("abcdef"); }); }); </script> </body> </html>
At this sample page, we want to :
if user go to /index.html, he will see “Hello World!” and don’t see the “abcdef” inside “h1” and then if user click “#test-btn” then he can see “Hello World!” replaced with “abcdef” and see the “abcdef” inside “h1”.
Now, let’s initialize the codecept by open separate console window and run :
vendor/bin/codecept bootstrap
Then, call command :
vendor/bin/codecept generate:cept acceptance Index
We then will get pre-filled configuration and make tests with folder named “tests”. What we need to do, is configure the ‘tests/acceptance.suite.yml’ file like the following :
# Codeception Test Suite Configuration # filename tests/acceptance.suite.yml class_name: AcceptanceTester modules: enabled: [WebDriver] config: WebDriver: url: 'http://localhost/codeceptiontest/' # our url base browser: firefox capabilities: unexpectedAlertBehaviour: 'accept'
Great! then we can create a test in ‘tests/acceptance/IndexCept.php’ :
// filename : tests/acceptance/IndexCept.php $I = new AcceptanceTester($scenario); $I->wantTo('see home page'); $I->amOnPage('/index.html'); $I->see('Hello World!'); $I->dontSee("abcdef", '//body/h1'); $I->click('#test-btn'); // $I->wait(1); // you can set delay $I->See('abcdef','//body/h1');
And finally! Run the test!
vendor/bin/codecept run acceptance
Done 😉
References :
1. http://jonnnnyw.github.io/php-phantomjs/
2. http://codeception.com/docs/modules/WebDriver
2 comments