Screenshots 2.0 – Replace PhantomJS with NightmareJS

PhantomJS is nice for really simple stuff like i showed in the last post. But if you try to do more complicated stuff like: Login first and then make a screenshot … you better try NightmareJS.

NightmareJS is a high level wrapper for PhantomJS and it’s much easier to control the browser with it. You install it with:

npm install nightmare

The new screenshot script starts like the other one with the configuration. But then you can simply work with for-loops.


var urls = [];
urls.push('http://www.redbull.com/');
urls.push('http://www.karriere.at/');

var sizes = [];
sizes.push({width: 1024, height: 768});
sizes.push({width: 768, height: 1024});
sizes.push({width: 640, height: 1024});
sizes.push({width: 320, height: 1024});

var clip = true;

var Nightmare = require('nightmare');

for(u in urls) {
    for(s in sizes) {
        var nightmare = new Nightmare();

        var size = sizes[s];
        var filename = 'screen-' + String("0" + u).slice(-2) + '-' + size.width + '.png';
        var url = urls[u];

        nightmare
            .viewport(size.width, size.height)
            .goto(url)
            .wait()
            .screenshot(filename)
            .run(function(err, nightmare){
                console.log('Done.');
            });
    }
}

But i want to do some stuff before i make the screenshot … like a login. There is a use()-method in NightmareJS intended for plugins, but i use it for an encapsulated sub-queue. In the navigate()-function i do the login and then return to the main-queue to take the screenshot.


var urls = [];
urls.push('http://www.karriere.at/jobs/javascript');
urls.push('http://www.karriere.at/');

var sizes = [];
sizes.push({width: 1024, height: 768});
sizes.push({width: 768, height: 1024});
sizes.push({width: 640, height: 1024});
sizes.push({width: 320, height: 1024});

var email = 'test@test.at';
var password = 'test@test.at-password';

var navigate = function(nightmare) {
    return function(nightmare) {
        nightmare
            .click('#userLogin')
            .wait(200)
            .type('#loginForm [name=email]', email)
            .type('#loginForm [name=password]', password)
            .click('#submitBtn button')
            .wait();
    };
}

var Nightmare = require('nightmare');

for(u in urls) {
    for(s in sizes) {
        var nightmare = new Nightmare();

        var size = sizes[s];
        var filename = 'screen-' + String("0" + u).slice(-2) + '-' + size.width + '.png';
        var url = urls[u];

        nightmare
            .viewport(size.width, size.height)
            .goto(url)
            .wait()
            .use(navigate(nightmare))
            .screenshot(filename)
            .run(function(err, nightmare){
                console.log('Done.');
            });
    }
}

PhantomJS – Responsive Screenshots for Different Websites

PhantomJS is a headless browser. That means: It is a full and working browser, you can use it from command line and you can control it by scripts … but it has no window for displaying the websites.

What for?

We use it for creating screenshots or you can test your website automatically because PhantomJS can everything that a normal Webkit browser can. You could also build a crawler for javascript-powered websites or you can write performance monitoring scripts.

Installation is really simple, just download the archive and extract it … voila! Now you can now use the bin/phantomjs.

Screenshots

Here the complete and working script, the screenshots will be in the same folder as the script. You can run it with:
bin/phantomjs screenshots.js


/**
 * Configuration
 */

var urls = [];
urls.push('http://www.redbull.com/');
urls.push('http://www.karriere.at/');
urls.push('http://www.michael-feichtinger.at/');

var sizes = [];
sizes.push({width: 1024, height: 768});
sizes.push({width: 768, height: 1024});
sizes.push({width: 640, height: 1024});
sizes.push({width: 320, height: 1024});

/**
 * if clip is true the screenshot will be cropped to that size
 * else phantomjs will screenshot the whole website
 */
var clip = true;


/**
 * All the logic
 */

var uIdx = 0;
var sIdx = 0;
var webpage = require("webpage");

function next(uIdx, sIdx) {
    if(uIdx < urls.length) {
        screenshot(uIdx, sIdx);
    } else {
        phantom.exit();
    }
}

function screenshot(uIdx, sIdx) {
    
    var size = sizes[sIdx];
    var filename = 'screen-' + String("0" + uIdx).slice(-2) + '-' + size.width + '.png';
    var url = urls[uIdx];

    var page = webpage.create();
    page.viewportSize = size;

    if (clip) {
        page.clipRect = { top: 0, left: 0, width: size.width, height: size.height };    
    }

    page.open(url, function(){
        return window.setTimeout((function() {
            page.render(filename);
            page.close();

            sIdx++;
            if(sIdx >= sizes.length) {
                sIdx = 0;
                uIdx++;
            }

            return next(uIdx, sIdx);
        }), 200);
    });
}

next(uIdx, sIdx);

Learning Go: Some Snippets …

… started to play a bit with go, the programming language from google. First step, trying to build a webapplication in go … first insights: it’s really fucking easy …

Tutorial: http://golang.org/doc/articles/wiki/

Load a config from json file:


import (
    "fmt"
    "encoding/json"
    "os"
)

type Config struct {
    Port string
}

func loadConfig() (Config) {
    
    file, err := os.Open("conf.json")
    if err != nil {
        fmt.Println("Error: " + err)
    }

    config := Config{}
    jsonParser := json.NewDecoder(file)
    if err = jsonParser.Decode(&config); err != nil {
        fmt.Println("Error: " + err)
    }

    return config
}

The JSON file …


{
    "port": "8080"
}

Simple Webserver


package main

import (
    "fmt"
    "net/http"
)

func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hi there, I love %s!", r.URL.Path[1:])
}

func main() {
    http.HandleFunc("/", handler)
    http.ListenAndServe(":8080", nil)
}

Render HTML Templates


import (
    "html/template"
)

func renderTemplate(w http.ResponseWriter, tmplName string, p *Page) {
    t, _ := template.ParseFiles("views/" + tmplName + ".html")
    t.Execute(w, p)
}

Global Javascript Error Logging with Google Analytics

Does my website really work for all users, browsers, versions? There are hundreds of possible combinations, it’s not possible to test all, but we can try to monitor all users and make errors visible you never would test for …

Make a global error handler and push all errors as events to Google Analytics. In Analytics you can then create some analyses about errors on browser/version or browser/os. Or you just go through the list and fix the errors in the specific browsers.

Here is the code for pushing javascript errors as Analytics events. To make it work you have to include the Analytics code-block in the website. This version is for classic Analytics, not for the new Universion Analytics.

 
function logErrorToAnalytics(errorMsg, url, lineNumber, column) {
    if(! _gaq) { return false; }
 
    var category = 'error';
    var url = url;
 
    var error = errorMsg + ' (' + lineNumber;
    if(column) {
        error += ':' + column;
    }
    error += ')';
 
    _gaq.push(['_trackEvent', category, url, error]);
}
 
window.onerror = function(errorMsg, url, lineNumber, column, errorObj) {
    try {
        logErrorToAnalytics(errorMsg, url, lineNumber, column);
        // console.log(errorMsg, url, lineNumber, column, errorObj);
    } catch(ex) {}
    return true; // hide real error
};

Check if it work in Live => Events with category filter “error”. Don’t forget to write an error in your script … else you wont see one in Analytics ๐Ÿ™‚

Bildschirmfoto 2014-06-28 um 09.37.10

Elasticsearch and node.js – Getting Started Example

Here a simple example how to use elasticsearch with node.js …

Add Document to Index

With client.create you can add a document to your index, if no id is given than some id is generated for you.
Submit ?title= and &tags= as GET params
eg.: http://127.0.0.1:1337/create?title=A%20nice%20title&tags=some,nice,tags

function create(query) {

    var title = query.title;
    var tags = query.tags.split(',');
    
    var D = new Date();
    var date = parseInt(D.getTime()/1000);

    client.create({
        index: 'myindex',
        type: 'mytype',
        // id: '1',
        body: {
            title: title,
            tags: tags,   
            published: true,
            published_at: date,
        }
    }, function (error, response) {
        // ...
    });

    eventEmitter.emit('doOutput', {message:'okay'});
}

Searching

Search in your index with q= url parameter …
eg.: http://127.0.0.1:1337/search?q=nice

function search(query) {

    var q = '';
    if (query.q) {
        q = query.q;
    }

    client.search({
        q: q,
    }).then(function (body) {
        eventEmitter.emit('doOutput', {message:'okay', hits:body.hits.hits});
    }, function (error) {
        console.trace(error.message);
    });
}

Generating output on event

Output your results or status messages …

function output(data) {

    response.writeHead(200, {'Content-Type': 'text/html'});

    if(data.hits) {
        str = '';
        for(i=0; i<data.hits.length; i++) {
            var hit = data.hits[i];

            var D = new Date(hit._source.published_at*1000);
            var published = util.format('%s-%s-%s',
                D.getFullYear(), D.getMonth(), D.getDate());

            str += util.format('%s - %s (Score: %s, ID: %s)',
                hit._source.title, published, hit._score, hit._id);
        }
        response.end(str+'\n');
    } else {
        response.end(data.message+'\n');
    } 
}

The server …

Require some stuff, start http server and simple routing.

var util = require('util');
var events = require('events');
var eventEmitter = new events.EventEmitter();
var http = require('http');
var url = require('url');
var elasticsearch = require('elasticsearch');
var client = new elasticsearch.Client({
    host: 'localhost:9200',
    log: 'trace'
});
var response = null;

eventEmitter.on('doOutput', output);


http.createServer(function(req, res) {

    response = res;
    var parsedUrl = url.parse(req.url, true);

    if(parsedUrl.pathname == '/create') {
        create(parsedUrl.query);
    } else if(parsedUrl.pathname == '/search') {
        search(parsedUrl.query);
    }

}).listen(1337, '127.0.0.1');

console.log('Server running at http://127.0.0.1:1337/');

More infos about elasticsearch.js here:

  • http://www.elasticsearch.org/guide/en/elasticsearch/client/javascript-api/current/index.html

More about eventEmitters here:

  • http://www.sitepoint.com/nodejs-events-and-eventemitter/

WordPress Custom Image Field in 80 lines

You need nearly 80 lines to add a custom file field to post edit screen (and create screen). There are some situations where it’s necessary to have more than one post image, for example if you want to have a separate image of facebook sharing (og:image).

The examples i found online are a bit complicated and often do more than just adding an image field, so here is the minimal code to do just add the field and save the image-url to post-meta.

Adding the field to admin is still simple and done in bohuco_edit_post and bohuco_image functions. There are two actions used, one for edit and the other for creating new posts. The bohuco_image formats the html for the form field, for this example i made it very simple.

 
    function bohuco_edit_post($post_id) {
        add_meta_box('bohuco_customImage', 'Custom Image', 'bohuco_image', 'post', 'normal', 'high' );
    }
    add_action('load-post.php', 'bohuco_edit_post');
    add_action('load-post-new.php', 'bohuco_edit_post');
 
    function bohuco_image($post, $field) {
        wp_nonce_field(plugin_basename(__FILE__), '_wpnonce_bohuco');
 
        if (! $value = get_post_meta($post->ID, $field['id'], true)) {
            $value = '';
        }
 
        echo '<input type="file" name="'.$field['id'].'_upload" size="55" /><br />';
        if ($value) {
            echo '<img src="'.$value.'" />';
        }
    }

Saving happens in bohuco_save_post where first the upload is done with build-in wordpress function wp_handle_upload, after that the url of the uploaded image is saved to a meta field (add_post_meta or update_post_meta). The overrides array holds some configuration for wp_handle_upload, the test_form must be false because it checks if the default upload action is used.

 
    function bohuco_save_post($postId) {
 
        if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
            return $postId;
        }
 
        if(isset($_FILES['bohuco_customImage_upload']['name'])
            && ! empty($_FILES['bohuco_customImage_upload']['name'])) {
 
            if (! wp_verify_nonce($_REQUEST['_wpnonce_bohuco'], plugin_basename(__FILE__))) {
                die('Security check'); 
            }
 
            $overrides = array('test_form'=>false);
            $result = wp_handle_upload($_FILES['bohuco_customImage_upload'], $overrides);
 
            if(isset($result['error']) && ! empty($result['error'])) {
                echo '<div class="error">'.$result['error'].'</div>';
            } else {
                $imageFileLocation = $result['url'];
                if (! update_post_meta($postId, 'bohuco_customImage', $imageFileLocation)) {
                    add_post_meta($postId, 'bohuco_customImage', $imageFileLocation, true);
                }                
            }
        }
    }
    add_action('save_post', 'bohuco_save_post');

This all works only if you first change the editor form enctype to multipart/form-data as you can see in add_post_enctype function. It’s a hack but it works, maybe there is a better method to change the post form tag?

 
    function add_post_enctype() {
        echo "<script type='text/javascript'>
                  jQuery(document).ready(function(){
                      jQuery('#post').attr('enctype','multipart/form-data');
                  });
              </script>";
    }
    add_action('admin_head', 'add_post_enctype');

Here is the whole example as little plugin, it should also work if you put it in a functions.php of a theme. I build it in wordpress 3.8 but should also work in older versions.

/*
Plugin Name: Custom Image Field
Plugin URI: http://bohuco.net/blog
Version: 1.0.0
Author: @DerFichtl
*/
 
if (is_admin()) {
 
    /**
     * Modify form enctype to support uploads
     */
    function add_post_enctype() {
        echo "<script type='text/javascript'>
                  jQuery(document).ready(function(){
                      jQuery('#post').attr('enctype','multipart/form-data');
                  });
              </script>";
    }
    add_action('admin_head', 'add_post_enctype');
 
    /**
     * Add custom field to new post and edit post screen
     */
    function bohuco_edit_post($post_id) {
        add_meta_box('bohuco_customImage', 'Custom Image', 'bohuco_image', 'post', 'normal', 'high' );
    }
    add_action('load-post.php', 'bohuco_edit_post');
    add_action('load-post-new.php', 'bohuco_edit_post');
 
    /**
     * Format the field html and output image if present
     */
    function bohuco_image($post, $field) {
        wp_nonce_field(plugin_basename(__FILE__), '_wpnonce_bohuco');
 
        if (! $value = get_post_meta($post->ID, $field['id'], true)) {
            $value = '';
        }
 
        echo '<input type="file" name="'.$field['id'].'_upload" size="55" /><br />';
        if ($value) {
            echo '<img src="'.$value.'" />';
        }
    }
 
    /**
     * Do upload and save custom meta field 
     */
    function bohuco_save_post($postId) {
 
        if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
            return $postId;
        }
 
        if(isset($_FILES['bohuco_customImage_upload']['name'])
            && ! empty($_FILES['bohuco_customImage_upload']['name'])) {
 
            if (! wp_verify_nonce($_REQUEST['_wpnonce_bohuco'], plugin_basename(__FILE__))) {
                die('Security check'); 
            }
 
            $overrides = array('test_form'=>false);
            $result = wp_handle_upload($_FILES['bohuco_customImage_upload'], $overrides);
 
            if(isset($result['error']) && ! empty($result['error'])) {
                echo '<div class="error">'.$result['error'].'</div>';
            } else {
                $imageFileLocation = $result['url'];
                if (! update_post_meta($postId, 'bohuco_customImage', $imageFileLocation)) {
                    add_post_meta($postId, 'bohuco_customImage', $imageFileLocation, true);
                }                
            }
        }
    }
    add_action('save_post', 'bohuco_save_post');
}

Things to improve would be: 1. Check if the uploaded file is an image, example works with all files allowed by wordpress. 2. Create a thumbnail of the image. 3. The save function is called twice if a new post is saved.

Do Some Things About Your WordPress Security … and Why

1. Install a Security Plugin

screenshot-1

That’s an easy one, install a plugin that checks your security settings and give you advice. For example “Better WP Security” tell you 20 things you should do to improve your WordPress security and you can do the most of them with just one mouse click.

It also ads database backup feature, login lockouts and ssl features. Check it out!

2. Delete Your Admin-User

There is a user with the username “admin” on every WordPress. You should create a new user with another username and admin-privileges, after that delete the “admin”-user and transfer all posts to the new user.

Why? Automatic hacking attempts depends on WordPress defaults, so many attacks assume that there is an admin-user and try to get his password and/or privileges. See also 5. “Make Things Complicated”.

3. Prevent Random Login Attempts

Look at your apache logs, you will usually find many login attempts with admin user to your blog. That’s automated brute-force attacks. You are quite secure now because you already removed your admin user but you can do even more. Add a second password inquiry to your login-screen with .htaccess. You have a two step login procedure on different software levels then.

Add this lines at the top of your .htaccess

AuthType Basic
AuthName "Restricted Area"
AuthUserFile /var/www/htpasswd/.htpasswd

<Files "wp-login.php">
  require valid-user
</Files>

Of course, you have to create a .htpasswd file with a user and password … and don’t use the same password for both levels please.

4. Check Your Salts and Keys

Everybody is talking about salts, “Adobe has no salts”, “LinkedIn hashes are not salted” and so on. What all this security guys are talking about? A salt is a random and secret string you should add to your password hash-generation so it’s much harder to decrypt passwords with rainbow tables.

In wp-config.php are 8 keys and salts, everyone of these should be a different random string. If there is something like “put your unique phrase here” … DO IT NOW! There is a salt generator from wordpress.org that makes it easy and secure.

define('AUTH_KEY',         '+W)!0iS266I@0TfT9jA4;qUtc!6W.D}4A !MEDZL0J +5#(l|_c=t`$$&F');
define('SECURE_AUTH_KEY',  'xN&y_3FlF4vtBXKXv+-x7z00 |spmsZX`Vt_bk]hie~U;OE/JiQcUO,5EL5-;%~I');
define('LOGGED_IN_KEY',    'SFgSKKIb7ZNP%+E)FJ fKbz-%?z`,M1#@o^GY lay;$![@+->4nc|6+1-~57*Dan');
define('NONCE_KEY',        '9-sa~o?7T&.|o_|KtUIQ)jyTSs!v+r&[|H9`+lb|#|T},],dF@_G+l,Y$2&lyJ]v');
define('AUTH_SALT',        '/Js]Ck2|5|smh.r%G!vjD+s3B7y2ECps3nV?qa.3M|*0K?B-=ZA:PH%uaP_Pk?bo');
define('SECURE_AUTH_SALT', 'Lo[Vcx5KAtIA|N9z]1ST_kLBYSr@-]^N96-UZaH1=SM]VQYJ)zVMHV|P`X:KY*8,');
define('LOGGED_IN_SALT',   'l3Pf#4o:U-6[3_QDf~;Pfz(E@jlA{5fs-2t,v|h`bKZ& =sY= .|t+,+:A*g/#uG');
define('NONCE_SALT',       '}<|(G ;p+vc/Y(jyfW*@9O|fK0qpZtKLw(47,M)0_J-.DYKA^]Q-gdUn|<+_Y^!h');

If everything looks okay or you just created new salts ... nevertheless you should change it just a bit, add some random chars to it, just for the case.

5. Make Things Complicated

There are some things you can do that it gets very complicated to automatically attack your wordpress. For example you can change your database-table-prefix to something random (not wp_), rename your wp-content folder or hide your wp-login.php file. All this things are not so easy to do, they may break your blog, so you should use "Better WP Security" or other plugins for that.

6. Check File Permissions

Okay it's a bit of work, but you should check you file permissions and set it to less as possible. There is no need that your config is always writable or that all your plugin folders have 777 permissions. Remove write-permissions wherever it's possible.

A hacker is looking for places where he can modify things, make it hard for him to find such places.

7. Deactivate The Theme- And Plugin-Editor

This one is a must! Deactivate the build-in WordPress file-editors under themes- and plugins-menu, the menu-point where you can edit php-files direct in your wordpress. If ever an attacker get into your wordpress he can modify all files where apache-user has write-permissions.

Add the following line to wp-config.php

define('DISALLOW_FILE_EDIT', true);

8. Clean Up a Bit

Delete everything you don't need! Old and unused plugins and themes, readme-files, wordpress sample config, old database dumps, uploaded zip files, old renamed files ... anything you don't need anymore. Move it to a folder, make a zip out of it for backup and then delete it.

You don't need this stuff anymore and there is a chance that in future someone discover a vulnerability in one of these files. If this day comes it's maybe to late to delete it, so you should do it now.

Linux “Find” 101 for Developers

Finding things on disk is a very important task, not only for sys-admins. Thank god there is a “find” command on every linux prompt. Here are the most important find options suitable for web-developers and programmers who needs to find something on their project folder or document root.

Linux Find Commandline Screenshot

Find Command Features

  • Finding files, folders, symlinks
  • Search Recursive in subfolders
  • Filter by user, groups, permissions, date/time
  • Change output format of results
  • Piping results to other commands

Find Files in Folders

# find . -name "*.php"
start searching in current directory, search for *.php files
# find . -name "Dat?.*"
Use wildcards like * and ? (files any extension, starting with “Dat” and then single wildcard char)
# find . -iname "DaTe.php"
search case insensitive (match Date.php, date.php, …)
# find . -follow -name "*.php"
follow symlinks into other directories
# find . -name "\.*"
find files starting with a dot (.svn, .htaccess)
# find . -name "[A-Z]*.php"
use shell patterns, find files starting with uppercase (Classes for example) use [a-z] for lowercase
# find . -user www-data
find files by files owner (a linux user)
# find . -group www-data
find files by files owner group
# find . -perm 777
find files and folder with all permissions
# find . -mindepth 1 -maxdepth 2 -name "*.php"
limit the search depth to subfolders
# find . -type d
limit search to directories (d) or files (f)
# find . -name "*.php" -printf "%p %k KB\n"
use printf for output formating (not available on OSX)
# find /var/www -name "*.php" -size +1M
find files by size (larger than 1 Megabyte)
# find /var/www -mmin -60
find files by modify date, -mmin -60 means within 60 minutes, +60 would mean: not in the last 60 minutes. use -mtime for days.
# find /var/www -amin -60
find files by access date/time

More about find command

  1. Find man page
  2. Ubuntu help on find command
  3. 35 practical examples of linux find

PHP Constants – Define, Use and Performance


Using constants is a good idea if you have values that never change. It makes code more secure because the values can’t be changed after defining and it makes code more readable. Here is all you should know about constants, some performance charts and a dirty secret.

A constant is case-sensitive by default. But a good convention for naming constants is: Always uppercase and separate words with underscores. Constants can only hold scalar values like integers, strings, boolean and float … no arrays.

define('DB_HOST', 'localhost');
define('DB_PORT', '8080');

// numbers works too if not at the beginning
define('DB1_PORT', 8000);

// get all defined constants (also php constants like: E_NOTICE)
var_dump(get_defined_constants());

// get user constants only
$allConstants = get_defined_constants(true); 
var_dump($allConstants['user']);

Define and Use Constants

You can use the define() function or the const keyword (since php 5.3) to create a constant. I usually use define() because the const keyword works only on the top level scope and not in if-statements or functions. Constants are global by default and therefor “bad” by nature (exception are class-constants we will see later).

If a constant is defined you can’t remove or modify it … obvious it’s a constant.

if (true) { 
    // this works 
    define('MY_CONST', 'my const');

    // Parse error ... not top level scope
    const MY_CONST = 'my const';
}

define('MY_CONST', 'try 1');
define('MY_CONST', 'retry'); // Notice: Constant MY_CONST already defined in ...
MY_CONST = 'wtf'; // Parse Error!

If you can use the const statement do it, const is twice as fast than define() function.

There is a constant() function if you don’t know the name of a constant, for example if you have to compute the constant-name. The constant() function is two-times slower as using a constant direct.

Checking for Constants

If you want to know if a constant is defined then use defined() function. If you don’t check first and just try to use a non existent constant you will get a: “Notice: Use of undefined constant”

if (defined('MY_CONST')) { // don't forget the quotes
    echo MY_CONST;
}

Dirty Secret: Use of Undefined Constant …

This is one of PHPs dirty secrets, if you try to use an undefined constant, php will create it on-the-fly and uses as value the name of the constant. IMHO you should never use this circumstance as a feature it throws also an php notice …

“Notice: Use of undefined constant myDirtyConstant – assumed ‘myDirtyConstant’ in …”

When this happens, it is usually an programming error, for example you forgot an dollar sign …

// MY_DIRTY_CONSTANT is not defined
if(! defined('MY_DIRTY_CONSTANT')) { 
    echo "Test: ".MY_DIRTY_CONSTANT; // ... but you can use it???
}

// Often happens in array-keys
$array = array();
$array[myKey] = "nice try"; // should be $myKey

Class Constants

Class constants are defined on top of the class with the const statement. You can use the constant with two colons like a static method or variable (without dollar).

class User {
    const TABLE = 'user';

    function getAll() {
        $sql = sprintf('SELECT * FROM %s', self::TABLE);
    }
}

The only way to get all defined class constants is using the reflection class.

$refl = new ReflectionClass('User');
var_dump($refl->getConstants());

CONSTANT vs. Variable

Surprising for me: Constants are much slower than variables, look at following chart about defining and using a variable / constant:


PHP String Replace Functions – Cheatsheet and Tutorial


PHP is famous for the simple and rich string modification methods. One important type of modification is to replace a string with another. Here are the possible methods, how to use it. At the end you will find a performance comparison over all the function and different input sizes.

Simple str_replace

str_replace('search string aka needle', 'replace with', 'in this string aka haystack');

Basic Example

$name = 'bill';
$replaceName = 'steve';
$string = 'bill is a genius.';

$newString = str_replace($name, $replaceName, $string); // => steve is a genius.

Get Replacement Count

// ... via pass-by-reference variable
str_replace('one','two','one one one', $count);
echo $count; // = 3

Use Arrays as $search and $replace

$search = array('a','b');
$replace = array('b','a');
echo str_replace($search, $replace, 'ababab'); // => aaaaaa

Insensitive str_replace Function

// str_replace is case-sensitive ... str_ireplace not
echo str_ireplace('A', 'b', 'aA'); // => bb

Use Strings like Arrays

$str = 'a string';
$str[0] = 'A'; // change first char
$str[strlen($str)] = '!'; // append a char

Use substr_replace for Replacing Strings by Index

$str = 'another string';

// insert a string on position 8 (overwrite 0 chars)
$str = substr_replace($str, 'nice ', 8, 0);

// overwrite 5 chars on pos 8 with the new string
$str = substr_replace($str, 'not nice ', 8, 5);

strtr Single Byte Replacements

echo strtr('this is a nice text', "t", "T"); // => This is a nice TexT

// a bit strange but ... replaces char by char ... all x=>X, t=>T, e=>E
echo strtr('this is a nice text', "xte", "XTE"); // => This is a nicE TEXT

// use it with associative arrays
echo strtr('this is a nice text', array("xt"=>"XT")); // this is a nice teXT

Swiss Army Knife preg_replace

echo preg_replace("/nice/", "very nice", "this is a nice text");

// replace with limit
echo preg_replace("/t/", "T", "this is a nice text", 1);

// replace with no limit
echo preg_replace("/t/", "T", "this is a nice text", -1);

// replace with $count variable
echo preg_replace("/t/", "T", "this is a nice text", -1, $count);

// replace with arrays, returns array("bbbbbb","bbbbbbb")
var_dump( preg_replace(array('/a/'), array('b'), array('ababab','bababa')) );

It’s getting complicated now … preg_replace_callback

// replace with callback, returns a with uppercase A
preg_replace_callback('/a/', function($match){
    return strtoupper($match[0]);
}, 'ababab');

// use create_function if you want a slow version (see below)
preg_replace_callback('/a/', create_function(
    '$matches',
    'return $matches[0];'),
'ababab');

“ereg_replace” is marked as deprecated and should not used anymore.

Here is the performance overview over the different replace functions with different parameters, avoid strtr with assoc array and preg_replace_callback with create_function it’s really slow.

How will the performance change if the data-size changes? Size matters but the count of replacements matters much more, many matches makes str_replace slow.

Here is the code template for the str_replace performance tests. microtime_float comes from php.net help pages.

$s = 'ababababab';
$a = 'cdcdcdcdcd';
$b = 'b';

$start = microtime_float();
for($i=0; $i<100000; $i++) {
    str_replace($a, $b, $s, $c);
}
echo "input 10b, search 10b, $c\t".round(microtime_float()-$start, 4)."\n";

Do you know more or better methods to replace strings ... share it in comments!


PHP CLI Simple HTTP-Server

Use PHP without Apache in dev-environments is very simple since PHP 5.4
DocumentRoot is the current directory or use the -t argument.

php -S localhost:8000

You can also provide a router-script …

php -S localhost:8000 router.php

PHP Docs

node.js EventEmitter example

Do some stuff at the same time and then emit events to sync it when everything is ready. For example load a layout html and a content html and combine it afterwards …

 
var http = require("http");
var url = require("url");
var fs = require('fs');
var events = require('events');
var eventEmitter = new events.EventEmitter();
 
http.createServer(function(request, response) {
 
    var pathname = url.parse(request.url).pathname;
    if(pathname == '/' || pathname == '') {
 
        // read file ... layout
        fs.readFile('./views/layout.html', {encoding:'utf8'}, function(err, html){
 
            sleep();
 
            if(err) console.log('Error:'+err);
            eventEmitter.emit('part', 'layout', html);
        });
 
        // read file ... content
        fs.readFile('./views/content.html', {encoding:'utf8'}, function(err, html){
            if(err) console.log('Error:'+err);
            eventEmitter.emit('part', 'content', html);
        });
 
 
        var parts = {};
        eventEmitter.on('part', function(key, value){
            parts[key] = value;
 
            // everything ready? 
            if(parts.layout && parts.content) {
                html = parts['layout'];
                html = html.replace('[content]', parts['content']);
 
                // output
                response.writeHead(200, {"Content-Type": "text/html"});
                response.write(html);
                response.end();    
            }
 
        });
    }
 
}).listen(8080);

Data-url as Favicon

Yes … works! Nice.

<link rel="icon" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDozNjJEMjA4M0ZDNzAxMUUyOTYyNEVGQTUwRkE5NEEwMyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDozNjJEMjA4NEZDNzAxMUUyOTYyNEVGQTUwRkE5NEEwMyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjM2MkQyMDgxRkM3MDExRTI5NjI0RUZBNTBGQTk0QTAzIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjM2MkQyMDgyRkM3MDExRTI5NjI0RUZBNTBGQTk0QTAzIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+nokApgAAAAZQTFRF////W3mYNQLL5gAAACBJREFUeNpiYAQDBiCAkiAGkInCG1UwqmBUwUhUABBgACi8AgG001DOAAAAAElFTkSuQmCC">

New on GitHub: Game Engine for Text Games

My Waiting Game Engine (WGE) is an simple php-engine for online browser games where the main task is to wait.

For each second you wait you get some money (depends on your level) and with money you can do/buy things. You can then combine the things and do more and more actions. For each action you earn experience (or lost some if you do the wrong action). And so on, and so on. More infos on GitHub.

Test a waiting game:
http://bohuco.net/waiting-game-engine/

Source code on GitHub:
https://github.com/DerFichtl/waiting-game-engine

 

PHP Selenium Test Hints

… also for sauce labs.

use local selenium server (chrome driver):

    public static $browsers = array(
    	array(
    		'browserName' => 'chrome',
    		'local' => true,
    		'sessionStrategy' => 'shared'
    	)
    );

explicit wait in test … just use php sleep:

    sleep(1);
    $this->url($this->pageUrl);
    sleep(1);

move the mouse (for onmousemove triggers in javascript)

    $element = $this->byCss('#jobfieldsSelect button');
    $this->moveto($element);

Wrench – The new HTML5 Websocket Class Hero for PHP

Wrench is an up-to-date websocket server implementation in php that works with current chrome and firefox versions. The library has no external dependencies, just php 5.3 is required. Some code says more than thousand words …

A simple server looks like …

 
#!/usr/bin/env php
<?php
 
require(__DIR__ . '/lib/SplClassLoader.php');
 
$classLoader = new SplClassLoader('Wrench', __DIR__ . '/lib');
$classLoader->register();
 
$server = new \Wrench\Server('ws://0.0.0.0:8080/', array(
    'allowed_origins' => array('bohuco.net'),
));
 
$server->registerApplication('echo', new \Wrench\Application\EchoApplication());
$server->run();

It’s important to bind the Socket on IP 0.0.0.0 … i first tried it with 127.0.0.1 then the socket is not reachable from external network. If you have a firewall like on Amazon EC2 you must configure it so, that the port (eg.: 8080) is open.

The actual application …

… is separated into another class/namespace … here is an example of a simple EchoApplication that echos the received message back:

 
namespace Wrench\Application;
 
use Wrench\Application\Application;
use Wrench\Application\NamedApplication;
 
class EchoApplication extends Application {
    public function onData($data, $client) {
 
        $client->send($data);
 
    }
}

The javascript part would look like this …

The last part (/echo) of the websocket address is the application name, this name must be registered in server.php with the registerApplication-method (see above).

 
if ("WebSocket" in window) {
    ws = new WebSocket("ws://46.51.177.248:8080/echo");
 
    ws.onopen = function() {
        ws.send('INIT');
    };
 
    ws.onmessage = function(evt) {
        console.log(evt.data);
    };
 
    ws.onclose = function(evt) {
    	ws.send('MESSAGE');
    };
 
    ws.onerror = function(evt) { }
 
} else {
	alert("WebSocket NOT supported by your Browser!");
}

More infos and documentation about Wrench:





Update WordPress and plugins with SFTP on Amazon EC2

On a FTP host wordpress updates are very straight forward, just fill your user credentials in the input fields and click the update-button. But what if you are on a amazon ec2 instance with SSH access only. There is no field for your private key or pem-file.

The wordpress plugin “SSH/SFTP Updater Support” adds a text-field and an upload-field for your Amazon EC2 private key to the install-/update-screen. So you can then use the updater for plugin updates and installs and even wordpress system updates.

Plugin Page:
http://wordpress.org/extend/plugins/ssh-sftp-updater-support/

How to Licence Apple Fonts for Web Usage

… not so easy as first thought: Buy a “Web-Licence” or “Web-Font” and use it on your page … thats not possible with apple fonts.

If you want browsersafe use one of more than 300 adobe webfonts like myriad, garamond or juniper you have to do it via typekit or webink. Just this two vendors can sell web licences for apple fonts.

 

Both services are easy to use: Just select the fonts you want to use and then insert the provided snippet in your webpage. Typekit uses javascript to deliver the font, webink do it with css-tag. So i like the webink delivery version more.

<!-- html head -->
<link href="http://fnt.webink.com/wfs/webink.css/
    ?project=CD0700EA-B4D9-4AFA-A52E-1F3AB19287CF
    &fonts=73E6C83D-7F13-A8AE-4770-C315AE5061C3:f=MyriadPro-Regular" rel="stylesheet" type="text/css"/>
/* in css */
@import url("http://fnt.webink.com/wfs/webink.css/
    ?project=CD0700EA-B4D9-4AFA-A52E-1F3AB19287CF
    &amp;fonts=73E6C83D-7F13-A8AE-4770-C315AE5061C3:f=MyriadPro-Regular");

The pricing is based ย on website usage … typekit on pageviews and webink on unique users. The prices goes from free to 120 USD per year for larger websites. More Infos you can find on the Adobe web fonts site.