phossa2/query

View on GitHub
src/Query/Misc/Parameter.php

Summary

Maintainability
A
25 mins
Test Coverage
<?php
/**
 * Phossa Project
 *
 * PHP version 5.4
 *
 * @category  Library
 * @package   Phossa2\Query
 * @copyright Copyright (c) 2016 phossa.com
 * @license   http://mit-license.org/ MIT License
 * @link      http://www.phossa.com/
 */
/*# declare(strict_types=1); */

namespace Phossa2\Query\Misc;

/**
 * Parameter
 *
 * Dealing with positioned or named parameters
 *
 * @package Phossa2\Query
 * @author  Hong Zhang <phossa@126.com>
 * @version 2.0.0
 * @since   2.0.0 added
 */
class Parameter
{
    /**
     * parameter pool
     *
     * @var    array
     * @access protected
     */
    protected $parameters;

    /**
     * The escape callable
     *
     * @var    callable
     * @access protected
     */
    protected $escape;

    /**
     * Returns a replacement placeholder for a $value
     *
     * @param  mixed $value
     * @return string
     * @access public
     */
    public function getPlaceholder($value)/*# : string */
    {
        // unique key with a pattern
        $key = uniqid('__PHQUERY_') . '__';
        $this->parameters[$key] = $value;
        return $key;
    }

    /**
     * Replace placeholders in the SQL with '?' or real value
     *
     * @param  string $sql
     * @param  array &$bindings
     * @param  array $settings
     * @return string
     * @access public
     */
    public function bindValues(
        /*# string */ $sql,
        array &$bindings,
        array $settings
    )/*# : string */ {
        // make sure escape function ok
        $this->setEscapeCallable($settings['escapeFunction']);

        // replace each placeholder with corresponding ?/:name/value
        return preg_replace_callback(
            '/\b__PHQUERY_[^_]++__\b/',
            function ($m) use (&$bindings, $settings) {
                return $this->replacePlaceholders($m[0], $bindings, $settings);
            },
            $sql
        );
    }

    /**
     * Replace '?' in the string with placeholders for value.
     *
     * e.q.  replace 'RANGE(?, ?)' with values [1, 10] etc.
     *
     * @param  string $string
     * @param  array $values
     * @access public
     */
    public function replaceQuestionMark(
        /*# string */ $string,
        array $values
    )/*# : string */ {
        $pat = $rep = [];
        foreach ($values as $val) {
            $pat[] = '/\?/'; // question mark
            $rep[] = $this->getPlaceholder($val); // placeholder
        }
        return preg_replace($pat, $rep, $string, 1);
    }

    /**
     * Replace each placeholder with '?', ':name' or value
     *
     * @param  string $key
     * @param  array &$bindings
     * @param  array $settings
     * @return string
     * @access protected
     */
    protected function replacePlaceholders(
        /*# string */ $key,
        array &$bindings,
        array $settings
    )/*# : string */ {
        $escape = $this->escape;

        $value = $this->parameters[$key];
        if ($settings['positionedParam']) {
            $bindings[] = $value;
            return '?';
        } elseif ($settings['namedParam'] && $this->isNamedParam($value)) {
            return $value;
        } else {
            return $escape($value);
        }
    }

    /**
     * Is it a named parameter ?
     *
     * @param  mixed $value
     * @return bool
     * @access protected
     */
    protected function isNamedParam($value)/*# : bool */
    {
        return is_string($value) && preg_match('/^:[a-zA-Z]\w*$/', $value);
    }

    /**
     * Set escape/quote method/function
     *
     * @param  null|callable $escapeFunction defult escape function
     * @return $this
     * @access protected
     */
    protected function setEscapeCallable($escapeFunction)/*# : callable */
    {
        if (!is_callable($escapeFunction)) {
            $this->escape = function ($v) {
                if (is_numeric($v) && !is_string($v)) {
                    return $v;
                } else {
                    return "'" . str_replace("'", "\\'", (string) $v) . "'";
                }
            };
        } else {
            $this->escape = $escapeFunction;
        }
        return $this;
    }
}