今日の小ネタ: partial()

PEP 309 のパクリ

<?php

function partial($func, $params, $append = false)
{
    if (!is_callable($func, false, $callable)) {
        trigger_error("$callable is not a valid function", E_USER_WARNING);
        return false;
    }

    if (!isset($GLOBALS['__PARTIAL_FUNCTION'])) {
        $GLOBALS['__PARTIAL_FUNCTION'] = array();
    }
    $n = count($GLOBALS['__PARTIAL_FUNCTION']);
    $GLOBALS['__PARTIAL_FUNCTION'][$n] = array($func);
    $GLOBALS['__PARTIAL_FUNCTION'][$n][] = is_array($params) ? $params : array($params);

    if ($append) {
        return create_function('', '$params = func_get_args();
            return call_user_func_array(
                $GLOBALS["__PARTIAL_FUNCTION"][' . $n . '][0],
                array_merge($params, $GLOBALS["__PARTIAL_FUNCTION"][' . $n . '][1])
            );'
        );
    } else {
        return create_function('', '$params = func_get_args();
            return call_user_func_array(
                $GLOBALS["__PARTIAL_FUNCTION"][' . $n . '][0],
                array_merge($GLOBALS["__PARTIAL_FUNCTION"][' . $n . '][1], $params)
            );'
        );
    }
}

if (isset($_SERVER['argv']) && realpath($_SERVER['argv'][0]) == __FILE__) {
    $log2 = partial('log', 2, true);
    var_dump($log2(256), log(256, 2));
}


PHP5 用にアレンジ

<?php

class Partial
{
    private $_prefix;
    private $_params;
    private $_append;

    public function __construct($prefix, $params, $append = false)
    {
        if (is_object($prefix)) {
            $this->_prefix = array($prefix);
        } elseif (!is_string($prefix)) {
            throw new Exception('Argument #1 should be an object or a string');
        } elseif (!strlen($prefix)) {
            $this->_prefix = '';
        } elseif (!preg_match('/^([A-Za-z_]?w*)(::)?$/', $prefix, $matches)) {
            throw new Exception("{$prefix} is not a valid function name prefix");
        } elseif (!empty($matches[2])) {
            if (!class_exists($matches[1])) {
                throw new Exception("Class {$matches[1]} does not exist");
            }
            $this->_prefix = array($matches[1]);
        } else {
            $this->_prefix = $prefix;
        }
        $this->_params = is_array($params) ? $params : array($params);
        $this->_append = (bool)$append;
    }

    public function __call($name, $args)
    {
        if (is_array($this->_prefix)) {
            $func = $this->_prefix;
            $func[] = $name;
        } else {
            $func = $this->_prefix . $name;
        }

        if (!is_callable($func, false, $callable)) {
            trigger_error("$callable is not callable", E_USER_WARNING);
            return;
        }

        if ($this->_append) {
            $args = array_merge($args, $this->_params);
        } else {
            $args = array_merge($this->_params, $args);
        }

        return call_user_func_array($func, $args);
    }
}


たとえばイメージ関数を OO ライクに使ったりできるようになります。

<?php
$rsrc = imagecreatetruecolor(120, 30);
if (!$rsrc) {
    exit;
}
$img = new Partial('image', $rsrc);
$white = $img->colorAllocate(255, 255, 255);
$black = $img->colorAllocate(0, 0, 0);
$img->fill(0, 0, $white);
$img->string(3, 5, 5, 'Hello, Partial!', $black);
$out = 'hello-partial.png';
if (file_exists($out)) {
    die("$out already exists");
}
$img->png($out, 9, PNG_ALL_FILTERS);


追記:
(array)$params は $params が null またはオブジェクトのとき想定外の挙動になるので、
該当箇所を is_array($params) ? $params : array($params) と修正しました。