Code

Coding, Programming & Algorithms, Tips, Tweaks & Hacks
Search

Using WebP image format for browsers that support it : Part 2

Almost a decade ago, I wrote about Using WebP image format for browsers that support it.
Now there's a different method to implement this, but not using CSS3 - rather, this is purely based on the new <picture> HTML5 tag.
HTML

<picture>
  <source srcset="https://mywebsite.com/images/cat.webp" type="image/webp">
  <img src="https://mywebsite.com/images/cat.png" loading="lazy"/>
</picture>
HTML 5

Safari which doesn't support webp will load cat.png while other browsers will load cat.webp.
If you are wondering what loading="lazy" is, check out https://web.dev/native-lazy-loading/ - currently supported only in Chrome.

Vanakkam !

PHP 7's Null Coalescing Operator

PHP 7's Null coalescing operator is more useful than I thought.

PHP
public static function read($id)
{
    $Row = MySQL::query("SELECT `Data` FROM `cb_sessions` WHERE `SessionID` = '$id'", TRUE);    
    
    # http://php.net/manual/en/function.session-start.php#120589
    //check to see if $session_data is null before returning (CRITICAL)                        
    
    if($Row['Data'] == false || is_null($Row['Data']))
    {
        $session_data = '';
    }
    else
    {
        $session_data = $Row['Data'];
    }
    
    return $session_data;    
}
PHP 7.4
can be replaced with :
PHP
public static function read($id)
{
    $Row = MySQL::query("SELECT `Data` FROM `cb_sessions` WHERE `SessionID` = '$id'", TRUE);    

    # Introduced in PHP 7 : https://stackoverflow.com/a/59687793/126833
    return $Row['Data'] ?? '';    
}
PHP 7.4
Vanakkam !

Get the element that triggered the event in an event handler function

A lot of people seem to get confused when trying to get the element that triggered the event in an event handler function. this or $(this) returns the parent element of the target.

Even before jQuery, vanilla JavaScript worked.

http://www.quirksmode.org/js/events_properties.html
JavaScript
function getTarget(e)
{
    var e = e || window.event, t;

    if (e.target) t = e.target;
    else if (e.srcElement) t = e.srcElement;

    if (t.nodeType == 3) // defeat Safari bug
    t = t.parentNode;

    return t;
}
JavaScript
Vanakkam !

Commas in HTML INPUT textfields for better readability

Sometimes, when a user enters numbers into a HTML INPUT text field, he/she enters commas too for readability esp in the case of financial inputs.

But when we send the input data via a form to the backend script for storing into a database, say MySQL database, we don't want the commas in the SQL INSERT statement.

So here's a script to show the commas while typing and remove the commas on hitting the submit button.


Vanakkam !

Encrypt and Decrypt strings in URL in PHP 7 using openssl

Here's a dead simple solution to pass through encrypted data as a value in your URL. Say you wanted to load something like https://my-product.com/loadProduct.php?id=1234 but don't want to show the real id as it may be the primary value in your database's table.

Instead, you can load it like this : https://my-product.com/loadProduct.php?id=33797030395539366e55673d and decrypt the value in your script.
PHP

<?php
define("ENCRYPTION_KEY", "123456*");
function encrypt($string) { return bin2hex(openssl_encrypt($string, 'BF-ECB', ENCRYPTION_KEY)); }
function decrypt($string) { return openssl_decrypt(hex2bin($string),'BF-ECB',ENCRYPTION_KEY); }
?>
PHP 7.2
More details on security : https://stackoverflow.com/a/50373095/126833
Vanakkam !

Always check for existence of a file in spl_autoload_register's function call

PHP
<?php
function my_autoloader_1($className)
{    
    $filename = 'test1/'.$className.'.php';
    if (file_exists($filename))
    {
        require_once $filename;
    }
    else
    {
        return false;
    }
}

# foo.php is in test2
function my_autoloader_2($className)
{    
    $filename = 'test2/'.$className.'.php';
    if (file_exists($filename))
    {
        require_once $filename;
    }
    else
    {
        return false;
    }    
}

spl_autoload_register('my_autoloader_1');
spl_autoload_register('my_autoloader_2');
 
$a = new foo();
?>
PHP 7
Vanakkam !

Availability of a file / link publically on Google Cloud Storage

If you want to check if a file exists publically on Google Cloud Storage,

PHP
<?php
$flyer = "http://commondatastorage.googleapis.com/bucket/path/to/directory/image.jpg";
$headers = @get_headers($flyer);
if ($headers[0] != "HTTP/1.1 200 OK" && $headers[0] != "HTTP/1.0 200 OK")
{
    # Load file locally / from elsewhere
    $flyer = IMG."/image.jpg";
}?>
PHP 5
Vanakkam !

Compressing HTML

Compress might not be the right word, as it doesn't really compress / zip the output HTML code, but combines all the HTML lines to a single line and removes trailing / leading whitespaces.

This has to be your index.php and am assuming you are routing all incoming page requests to index.php which will be handled by either rewrites in htaccess or direct query strings. For example, I would have either

RewriteRule ^about\/vision$ index.html?page=about-vision [QSA,L]
or
RewriteRule ^register$ index.php?module=Register

PHP
<?php
ob_start();
require_once "index.phtml";
$html = ob_get_clean();
$htmls = explode("\n", $html);
for ($i = 0; $i < count($htmls); $i++)
{
    $htmls[$i] = trim($htmls[$i]);
}
$html = implode("", $htmls);
echo $html;
?>
PHP 5

There is one gotcha in this. You can't use single line comments start with // in inline Javascript code in the HTML. Either place the JavaScript code outside of the HTML page, in separate .js page or use multi-line comments /* */

Vanakkam !

Convert CSV to JSON

Sometimes I find myself converting google doc spreadsheets (CSV) to JSON format. I would later on use the JSON string to insert it into a JavaScript file.
So here is a simple script to convert a CSV file to JSON format.

Python
import sys, csv, json

if len(sys.argv) == 1:
    print "1 argument for filename required"
    sys.exit()

gdoc = csv.reader(open(sys.argv[1]))

# Get the 1st line, assuming it contains the column titles
fieldnames = gdoc.next() 

# Get the total number of columns
fieldnames_len = len(fieldnames)

data = [] # Empty list
i = 0

for row in gdoc:
    
    # Add an empty dict to the list
    data.append({})
    
    for j in range(0, len(row)):
        data[i][fieldnames[j]] = row[j]
    
    # What if the last few cells are empty ? There may not be commas
    for j in range(len(row), fieldnames_len):
        data[i][fieldnames[j]] = ""
    
    i = i + 1

print json.dumps(data)
sys.exit()
Python 2.7.2
Vanakkam !

Using WebP image format for browsers that support it

The WebP image format for the web is starting to get a lot of hype for its massive file compression. Chrome supports it since ver 9 and Opera since ver 11.10.
Image file sizes are reduced by more than 50% in WebPs when compared to JPGs.
Looking at Google Analytics on various sites, Chrome 9+ users constitute 20% - 25%. So providing one-fifth or one-fourth of the users with a much reduced page load is worth the extra effort.

1. Convert JPGs / PNGs to WebPs :

for i in *.jpg; do j=`echo "$i" | cut -d . -f 1`; convert -colorspace RGB "$i" "$i"; ~/libwebp/cwebp -q 75 "$i" -o "${j}.webp"; done

convert is used to convert the colourspace from RGB to CMYK since as of now, webp encoder doesn't support CMYK colourspace.

2. Use 2 different CSS files for background images - one for standard jpg/png and another for webp. Chrome 9+ and Opera 11.10+ would be served webp.css while all others img.css.

get_browser is pretty useful for browser detection, but requires to set browscap in php.ini which is a PHP_INI_SYSTEM directive and can't be set in using ini_set(), .htaccess or user php.ini which rules out almost all shared hosting enviroments.
But interestingly, this is possible in WebFaction where I could set browscap in a user php.ini file which was parsed.

PHP
<?php
$imgCSS = "img";
$browser = get_browser(null, true);
if (($browser['browser'] = "Chrome" && $browser['version'] >= 9) || ($browser['browser'] = "Opera" && $browser['version'] >= 11.10))
$imgCSS = "webp";
?>
.
.
.
<link rel="stylesheet" type="text/css" media="all" href="<?php echo $imgCSS ?>.css" />
PHP 5.2

If you are getting something like ...

<b>Warning</b>:  get_browser() [<a href='function.get-browser'>function.get-browser</a>]: browscap ini directive not set in <b>/path/script.php</b> on line <b>7</b>

and if there is no way to set browscap, then you need to find an alternative browser detection like phpbrowscap.

I benchmarked this on a graphically heavy wordpress site, having 11 photographs in a slider on the homepage which takes a megabyte alone.

BrowserRequestsSizeTime
FireFox 3.6311.9 MB21.48s (onload: 20.62s)
Chrome 1132577.90KB6.51s (onload: 6.51s, DOMContentLoaded: 2.88s)

(I could not find the option to view total bytes downloaded in Opera dragonfly's network tab)

Side Note : When uploading webp images to google storage, S3 or any other cloud service, specify the content-type / mime-type.

gsutil -h "Cache-Control:public,max-age=31536000" -h "Content-Type: image/webp" cp -a public-read kitten.webp gs://[bucket]/images/kitten.webp
s3cmd put kitten.webp s3://[bucket]/images/ --acl-public --mime-type "image/webp" --add-header="Cache-Control:max-age=315360000"
Vanakkam !