Variable number of parameters on a prepared statement in PHP

Sometimes, you need to use a different number of parameters for a prepared statement. The call_user_func_array function allows you to call bind_param with a varying number of parameters.

PHP

// Start by validating your data
$something filter_input(INPUT_POST'something'FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => '/^(this|that|other|thing)$/i']]);
 
// Set up the types string
$types '';
 
// Initialize the array the variables will be referenced from
$values = [];
 
// Create the base SQL statement
$query 'SELECT * FROM table';
 
// Initialize an array to store the WHERE comparisons
$where = [];
 
// Check to see if $something should be used 
// empty tests for both null (no data in input) and false (invalid data)
if (!empty($something)) {
   
    // Set the WHERE comparison for something
    $where[] = 'something = ?';
 
    // Append the type for this comparison
    $types .= 's';
 
    // Add a reference to the $something variable (that's what the ampersand is)
    $values[] = &$something;
 
}
 
// If the $where array has elements, add them to the query
if (count($where) > 0) {
    $query .= ' WHERE '.implode(' AND ',$where);
}
 
$stmt $mysqli->prepare($query);
 
// Create the array of parameters which will be sent to the bind_param method
$params array_merge([$types],$values);
 
// Bind the variables
call_user_func_array([$stmt,'bind_param'],$params);

What Branch am I on Anyway?

Often during web development the code is switching between different branches and it can be difficult to let everyone on a small team know which branch is in use.

Adding this line to a PHP script and using CSS to make it easy to see can save time.

HTML

<div class="git-branch">'.`git name-rev --name-only HEAD`.'</div>

Be sure to wrap it in some code to prevent display on the production site.

One Approach to Complying with a "script-src 'self'" Content Security Policy

Link: http://www.html5rocks.com/en/tutorials/security/content-security-policy/

I recently encountered this error when working with plugin code on an application:

Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'self' chrome-extension-resource:".

The cause of the error was inline script code I was using to pass values from the server to the client.

After a bit of research (see the link above), the best solution looked like a little bit of PHP code to create the JavaScript required to pass the values to the client.

The overhead of checking the timestamp and creating the file is minimal, so this code recreates the JavaScript once each day.

PHP

<?php
 
Class CSP {
    const JSFILENAME 'csp.js';
 
    static public function cspFilename($dir __DIR__) {
        return $dir.'/'.self::JSFILENAME;
    }
 
    static public function cspFileNeedsRebuild($filename) {
        if (!is_file($filename)) {
            return true;
        }
        $fileLastModified date('z',filemtime($filename));
        $today date('z');
        return $fileLastModified !== $today;
    }
}
 
$someValue 'Some value';
$jsFilename CSP::cspFilename();
if (CSP::cspFileNeedsRebuild($jsFilename)) {
    $js 'var someValue = "'.$someValue.'";'.PHP_EOL;
    file_put_contents($jsFilename,$js);
}
echo '<script src="'.$jsFilename.'"></script>';

Other solutions I could have used would have been to disable the Content Security Policy, but that's really a stupid approach. There is also nonce and one may code the policy with more complex values.

Presenting KnpMenus with Dojo

This Dojo module will traverse a menu delivered by the KnpMenuBundle and present it as a Dijitized menu bar.

There may be a few extraneous modules included, but this code will likely be extended.

Code

define([
    "dojo/_base/declare",
    "dojo/dom",
    "dojo/dom-attr",
    "dojo/dom-construct",
    "dojo/on",
    "dojo/query",
    "dijit/registry",
    "dijit/MenuBar",
    "dijit/MenuBarItem",
    "dijit/PopupMenuItem",
    "dijit/PopupMenuBarItem",
    "dijit/MenuItem",
    "dijit/DropDownMenu",
    "dijit/form/Button",
    "dijit/Dialog",
    "app/lib/common",
    "dojo/i18n!app/nls/core",
    "dojo/NodeList-traverse",
    "dojo/domReady!"
], function (declare, dom, domAttr, domConstruct, on, query, registry,
        MenuBar, MenuBarItem, PopupMenuItem, PopupMenuBarItem, MenuItem, DropDownMenu, Dialog,
        lib, libGrid, core) {
 
    function run() {
        var menuBar = new MenuBar({}, "admin-top-menu");
 
        function createMenuItem(widget, parent, depth) {
            var children, node, item, i, label, nextNode;
            var popupMenuObj, labelObj, link;
            children = query(parent).children();
            for( i = 0; i < children.length; i++ ) {
                node = children[i];
                nextNode = (typeof children[i + 1] !== "undefined") ? children[i + 1] : null;
                switch( node.tagName ) {
                    case "SPAN":
                    case "A":
                        label = node.textContent.trim();
                        if( typeof node.href !== "undefined") {
                            link = node.href;
                        } else {
                            link = null;
                        }
                        if( nextNode !== null && nextNode.tagName === "UL" ) {
                            popup = new DropDownMenu();
                            popupMenuObj = {label: label, popup: popup};
                            if( depth <= 1 ) {
                                item = new PopupMenuBarItem(popupMenuObj);
                            } else {
                                item = new PopupMenuItem(popupMenuObj);
                            }
                            createMenuItem(popup, nextNode);
                        } else {
                            labelObj = {label: label};
                            if( depth <= 1 ) {
                                item = new MenuBarItem(labelObj);
                            } else {
                                item = new MenuItem(labelObj);
                            }
                        }
                        if( link !== null ) {
                            item.on("click", function () {
                                location.href = link
                            });
                        }
                        widget.addChild(item);
                        break;
                    case "LI":
                        createMenuItem(widget, node, depth + 1);
                        break;
                }
            }
        }
 
        var menuElements = query("#admin-top-menu ul");
        if( menuElements.length > 0 ) {
            createMenuItem(menuBar, menuElements[0], 0);
            domConstruct.destroy(menuElements[0]);
        }
        menuBar.startup();
    }
    return {
        run: run
    };
});

Easing JavaScript Development with Builds under Symfony with Assetic

Dojo has a great build process which allows you to create a optimized and minimized files for the client side (and more!).

In a production environment, this greatly improves performance.

However, building the code after every change will slow development significantly.

Since Dojo can load the required modules dynamically, you can load the source files and work with them directly, and maintain your profile file as you work. Running a build at the end of each development session will help to ensure the code and profile stay in sync.

Assetic can be used to switch between the source and built version of files by placing the JavaScript configuration in config_dev.yml and config_prod.yml. The templates don't need to be modified.

config_dev.yml

Code

assetic:
    use_controller: true
    assets:
        dojo_js:
            inputs:
                - 'common/dojo-release-1.10.4-src/dojo/dojo.js'
        main_js:
            inputs:
                - 'common/app/main.js'
        admin_js:
            inputs:
                - 'common/app/admin/user.js'

config_prod.yml

Code

assetic:
    assets:
        dojo_js:
            inputs:
                - 'common/release/dojo/dojo.js'
        main_js:
            inputs:
                - 'common/release/app/main.js'
        admin_js:
            inputs:
                - 'common/release/app/admin.js'

twig template that references the Assetic assets.

page_footer_script.html.twig

Code

{% javascripts
    '@dojo_js'
    '@main_js'
     %}
    <script src="{{ asset_url }}"></script>
    {% endjavascripts %}

@admin_js is specific to only the admin pages, it would be referenced there.

:: Next >>