Skip to content

Instantly share code, notes, and snippets.

@Leko
Last active April 24, 2016 06:41
Show Gist options
  • Save Leko/6665a57aab9a5d3ae5cb to your computer and use it in GitHub Desktop.
Save Leko/6665a57aab9a5d3ae5cb to your computer and use it in GitHub Desktop.
PHPでの便利なスニペット、関数、クラス
<?php
// 欲しいメソッドのインタフェースだけ書いていく
// isset + デフォルト値
// 階層は.で潜るFuel式が手軽そう
// http://fuelphp.com/docs/classes/arr.html#/method_get
Array::get(array $source, $key, $default = null)
// Lodashのconcat
// https://lodash.com/docs#concat
Array::concat(array $source, $merge_arrays...)
// Lodashのfind
// https://lodash.com/docs#find
// @param array|callable $predicate 関数もしくは$key = $valueと見なすための連想配列
Array::find(array $source, $predicate)
// Lodashのreject
// https://lodash.com/docs#reject
// @param array|callable $predicate 関数もしくは$key = $valueと見なすための連想配列
Array::reject(array $source, $predicate)
// > エントリを一つだけ取得する場合、 array_rand() はランダムなエントリのキーを返します。
// > http://php.net/manual/ja/function.array-rand.php
// の糞仕様を解消して配列内の値をランダムに1つ返す
Array::rand(array $source)
// 対象の配列が連想配列か否かを返す。添字配列ならfalse
Array::isAssoc(array $source)
// LodashのgroupBy
// https://lodash.com/docs#groupBy
// @param array|callable $predicate 関数もしくは$key = $valueと見なすための連想配列
Array::groupBy(array $source, $predicate)
// in_arrayの第三引数が常時trueになってる版
Array::includes(array $source, $value)
// FuelのArr::pluck
Array::pluck(array $source, $key)
// Underscore.jsのpickメソッド
// http://underscorejs.org/#pick
Array::pick(array $source, array $pick_keys)
// 連想配列から指定されたキーを除外した値を返却する
Array::omit(array $source, array $omit_keys)
// issetの代わり
Array::has(array $source, $key)
// issetの代わり + 複数キーを一気に調べる
Array::hasAll(array $source, array $keys)
// Ruby2.3のdigメソッド
Array::dig(array $source, $keys...)
// RubyのEnumerable#grep
// http://ref.xaio.jp/ruby/classes/enumerable/grep
Array::grep(array $source, $value)
// ↑のキーを対象にした版(連想配列を想定)
Array::grepKeys(array $source, $value)
// 暇があったら実装もする
// newしても使える、継承しても使える、staticでも使えるの3タイプを一気に提供する
class Array extends \ArrayObject {
private $source;
// ただのarray, ArrayObject, ArrayAccessインタフェースを考慮しタイプヒント無し
public function __construct(array $source) {
$this->source = $source;
}
public static function __callStatic($name, array $args) {
$source = $args[0];
$args = array_slice($args, 1);
$instance = new static(array $source);
return call_user_func_array(array($instance, $name), $args);
}
}
<?php
class CallCounter {
private $target;
private $counts = array();
public static function stub(&$target)
{
new self($target);
}
public function __construct(&$target)
{
$this->target = $target;
$target = $this;
}
public function __call($method, $args)
{
if(!isset($this->counts[$method])) {
$this->counts[$method] = 0;
}
$this->counts[$method]++;
return call_user_func_array(array($this->target, $method), $args);
}
public function calledOnce($method)
{
return ($this->counts[$method] === 1);
}
}
// class A {
// public function hoge()
// {
// return 1;
// }
// }
// $a = new A();
// $c = new CallCounter($a);
// var_dump($a->hoge());
// var_dump($c->calledOnce('hoge'));
<?php
/**
* PHPの配列をOOPぽく扱えるクラス
*/
class Collection extends ArrayObject
{
private $raw;
public function __construct(array $list)
{
$this->raw = $list;
}
public function getIterator()
{
$class = $this->getIteratorClass();
return new $class($this->raw);
}
// 自前で実装すればそれを使う
public function sort($sort_flags = SORT_REGULAR)
{
sort($this->raw, $sort_flags);
return new static($this->raw);
}
// でなければ標準関数を探しに行く
public function __call($method, array $args)
{
if (method_exists($this->raw, $method)) {
$method = [$this->raw, $method];
} elseif (!function_exists($method)) {
$method = 'array_'.self::toSnakeCase($method);
array_unshift($args, $this->raw);
if (!function_exists($method)) {
throw new InvalidArgumentException("Undefined method {$method}");
}
}
if (in_array($method, self::$referenceExpects)) {
call_user_func_array($method, $args);
return new static($this->raw);
} else {
return new static(call_user_func_array($method, $args));
}
}
private function toSnakeCase($str)
{
$result = '';
for ($i = 0; $i < strlen($str); $i++) {
if (ctype_upper($str[$i])) {
$str[$i] = '_'.strtolower($str[$i]);
}
$result .= $str[$i];
}
return $result;
}
}
// Usage
// $list = new Collection([3, 2, 1, 1, 2, 3]);
// echo "count:{$list->count()}\n";
// echo "uniqued count:{$list->unique()->count()}\n";
// echo "\n";
// foreach ($list->unique()->sort() as $i => $num) {
// echo "{$i} => {$num}(unique + sort)\n";
// }
// echo "\n";
// foreach ($list->unique()->sort()->reverse() as $i => $num) {
// echo "{$i} => {$num}(unique + sort + reverse)\n";
// }
<?php
// (PHP 4 >= 4.1.0, PHP 5, PHP 7)
// cal_days_in_month — 指定した年とカレンダーについて、月の日数を返す
// http://php.net/manual/ja/function.cal-days-in-month.php
//
// > 年と月の順序に注意
// 年と月の順序に注意
cal_days_in_month(CAL_GREGORIAN, $month, $year);
// カレンダー定数
// http://php.net/manual/ja/calendar.constants.php
// MySQLのタイムゾーンの設定を確認する
// SELECT @@global.system_time_zone, @@global.time_zone, @@session.time_zone;
// (PHP 5 >= 5.2.0, PHP 7)
// DateTimeZone::listIdentifiers -- timezone_identifiers_list — すべてのタイムゾーン識別子を含む配列を返す
// http://php.net/manual/ja/datetimezone.listidentifiers.php
$timezones = DateTimeZone::listIdentifiers();
// UTC時刻をJST時刻へ変換する
// php.iniのdate.timezoneを変えてしまうとシステム全体の時刻に影響してしまう。
// そうではなくDB上はUTCにしておき、取り出して表示するときにオフセットを噛ませたい。
//
// (PHP 5 >= 5.2.0, PHP 7)
// DateTimeZone::__construct -- timezone_open — 新しい DateTimeZone オブジェクトを作成する
// http://php.net/manual/ja/datetimezone.construct.php
// 指定したタイムゾーンが無効な形式だった場合に Exception をスローします。
$zone = new DateTimeZone('Asia/Tokyo'); // この名前なりIDなりをDBに持っておいて渡す
// (PHP 5 >= 5.2.0, PHP 7)
// DateTime::__construct -- date_create — 新しい DateTime オブジェクトを返す
// http://php.net/manual/ja/datetime.construct.php
$datetime = new DateTime('now', $zone);
// (PHP 5 >= 5.3.0, PHP 7)
// DateTimeZone::getLocation -- timezone_location_get — タイムゾーンの位置情報を返す
// http://php.net/manual/ja/datetimezone.getlocation.php
// > Array
// > (
// > [country_code] => JP
// > [latitude] => 35.65444
// > [longitude] => 139.74472
// > [comments] =>
// > )
$zone->getLocation();
// (PHP 4, PHP 5, PHP 7)
// checkdate — グレゴリオ暦の日付/時刻の妥当性を確認します
// http://php.net/manual/ja/function.checkdate.php
checkdate ( int $month , int $day , int $year )
<?php
// ファイル操作の便利クラス
// PHP5.1でもSplFileInfoは存在するが、一部欠けているメソッドがある。
// [#getLinkTarget](http://php.net/manual/ja/splfileinfo.getlinktarget.php)は必要性が無いので未実装。
class File extends SplFileInfo {
/**
* @see http://php.net/manual/ja/function.file-exists.php
*/
public static function exists($filename)
{
return file_exists($filename);
}
/**
* @see http://php.net/manual/ja/function.glob.php
*/
public static function glob($pattern, $flags = 0)
{
return glob($pattern, $flags);
}
/**
* @see http://php.net/manual/ja/function.copy.php
*/
public function copy($dest, $context = null)
{
// デフォ値の記載がないためcontextが省略された場合引数を渡さないようにしている
if(is_resource($context)) {
return copy($this->getRealPath(), $dest, $context);
} else {
return copy($this->getRealPath(), $dest);
}
}
/**
* @see http://php.net/manual/ja/function.rename.php
*/
public function rename($newname, $context = null)
{
// デフォ値の記載がないためcontextが省略された場合引数を渡さないようにしている
if(is_resource($context)) {
return rename($this->getRealPath(), $newname, $context);
} else {
return rename($this->getRealPath(), $newname);
}
}
/**
* @see http://php.net/manual/ja/function.unlink.php
*/
public function remove()
{
return unlink($this->getRealPath());
}
/**
* @see http://php.net/manual/ja/splfileinfo.getextension.php
*/
public function getExtension()
{
return pathinfo($this->getRealPath(), PATHINFO_EXTENSION);
}
/**
* @see http://php.net/manual/ja/splfileinfo.getrealpath.php
* @see http://php.net/manual/ja/function.realpath.php
*/
public function getRealPath()
{
return realpath($this->getPath().DIRECTORY_SEPARATOR.$this->getFilename());
}
}
<?php
// こわくない関数型のスライドを思い出して
function picker($key) {
return function($item) use ($key) {
return is_array($item) ? $item[$key] : $item->{$key};
};
}
// メソッド名と引数を渡し、受け取ったオブジェクトのメソッドをコールする関数を返す関数
function invoker($method, array $args = []) {
return function($obj) use ($method, $args) {
return call_user_func_array([$obj, $method], $args);
};
}
// callableな配列を受け取り、各要素を用いてarray_reduceを実行する関数
function reducer(array $funcs) {
return function(...$initial) use ($funcs) {
return array_reduce($funcs, function($current, callable $fn) {
return call_user_func_array($fn, is_array($current) ? $current : [$current]);
}, $initial);
};
}
<?php
// マルチバイト、文字列操作関連の便利クラス
// マジックナンバーホイホイ。どうにかしたい
// http://php.net/manual/ja/function.mb-convert-kana.php
<?php
// 格子状の2次元配列を扱うためのクラス
// CSVなどのテーブル上のデータにも有用
class Matrix implements Iterator {
private $y = 0;
private $matrix;
// Traversableインタフェースと共存するためarrayタイプヒントは付けない
public function __construct($matrix)
{
$this->matrix = $matrix;
}
public function width()
{
if($this->has(0, 0)) {
return count($this->matrix[0]);
} else {
return 0;
}
}
public function height()
{
return count($this->matrix);
}
// x, yの順にしてしまうと実際の添字アクセスのときと逆になりイメージしにくいのでy, xの順にする
public function get($y, $x, $default = null)
{
if($this->has($y, $x)) {
return $this->matrix[$y][$x];
} else {
return $default;
}
}
public function set($y, $x, $value)
{
if(!isset($this->matrix[$y])) {
$this->matrix[$y] = array();
}
$this->matrix[$y][$x] = $value;
}
public function has($y, $x)
{
return isset($this->matrix[$y][$x]);
}
public function toAssoc(array $mapping_keys)
{
$results = array();
foreach($this->matrix as $row) {
$results[] = array_combine($mapping_keys, $row);
}
return $results;
}
// -> mixed
public function current()
{
return $this->matrix[$this->y];
}
// -> scalar
public function key()
{
return $this->y;
}
// -> void
public function next()
{
$this->y++;
}
// -> void
public function rewind()
{
$this->y = 0;
}
// -> boolean
public function valid()
{
return ($this->y < $this->height());
}
}
// ## Example
// ```php
// $m = new Matrix([
// [1,2,3],
// [4,5,6],
// [7,8,9],
// ]);
//
// foreach($m as $row) {
// foreach($row as $cell) {
// var_dump($cell);
// }
// }
// ```
<?php
/**
* mixin的なもの
*
* わざわざ各クラスにプロキシ用の__callメソッドを定義する必要がなくなる
*/
class Proxy
{
private $source;
private $targets;
public function __construct()
{
$this->targets = func_get_args();
}
public function __call($method, array $args = [])
{
foreach ($this->targets as $target)
{
if (!method_exists($target, $method)) continue;
return call_user_func_array([$target, $method], $args);
}
throw new InvalidArgumentException("Undefined method: {$method}");
}
}
class B {
public function foo() { return __FUNCTION__; }
}
class A {
public function hoge() { return __FUNCTION__; }
}
// Usage
// $proxy = new Proxy(new A, new B);
// var_dump($proxy->hoge());
// var_dump($proxy->foo());
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment