Skip to content

Instantly share code, notes, and snippets.

@chiyoyo
Last active April 26, 2016 16:30
Show Gist options
  • Save chiyoyo/43b87c1bf6e1429a9b67 to your computer and use it in GitHub Desktop.
Save chiyoyo/43b87c1bf6e1429a9b67 to your computer and use it in GitHub Desktop.
汎用ツリー構造クラスを作ってみた ref: http://qiita.com/chiyoyo/items/f06e4d3f3e39d4238bca
/**
* 汎用ツリー構造クラス
*/
<?php
class TreeNode
{
/** 本オブジェクトの親要素 */
private $parent;
/** 本オブジェクトの子要素 */
private $children = [];
/** 名前 */
private $name;
/** 値 */
public $value;
/**
* コンストラクタ
*
* @param string $name 名前(親の場合は省略可能)
*/
public function __construct($name = null)
{
$this->name = $name;
}
/**
* 子を追加する
* すでに同名のものがある場合は追加しない
*
* @param string $name 追加する子要素
* @return TreeNode 追加した子要素
*/
public function append($name)
{
$child = $this->child($name);
if ($child === false) {
$child = new TreeNode($name);
$child->parent = $this;
$this->children[$child->name] = $child;
}
return $child;
}
/**
* 指定した子を削除する
*
* @param string $name 削除する子要素
* @return TreeNode 自分自身
*/
public function remove($name)
{
if ($this->has($name)) {
unset($this->children[$name]);
}
return $this;
}
/**
* 指定した名前の子要素を取得する
*
* @param string $name 名前(省略時は全部)
* @return TreeNode | array | boolean
*/
public function child($name = null)
{
// 引数省略時は全部
if (is_null($name)) {
return $this->children;
}
// 引数指定時は指定した名前の子要素
if (!$this->has($name)) {
return false;
}
return $this->children[$name];
}
/**
* 指定した名前の子要素があるかどうか
* 省略時は子要素自体があるかどうか
*
* @param string $name 名前
* @return boolean
*/
public function has($name)
{
// 引数省略時
if (is_null($name)) {
return (0 < count($this->children));
}
return isset($this->children[$name]);
}
/**
* 名前を取得
* @return string
*/
public function name()
{
return $this->name;
}
/**
* 親要素を取得
*
* @return TreeNode 親要素
*/
public function parent()
{
return $this->parent;
}
/**
* パスの配列を取得する
* valueは含まれません
* 例: [NULL, 'HOGE', 'FUGA']
*
* @return array
*/
public function path()
{
$result = [$this->name];
$parent = $this->parent;
while($parent != null) {
$result[] = $parent->name;
$parent = $parent->parent;
}
$result = array_reverse($result);
return $result;
}
}
// 親ノード作成
$tree = new TreeNode();
// 子ノードを追加して子の値を設定
// 親 > HOGE = 123
$tree->append('HOGE')->value = 123;
// 子ノードを追加して、更にその下に子ノードを追加して
// 親 > HOGE > FUGA
$tree->append('HOGE')->append("FUGA");
// 子ノードHOGEの下にある子ノードFUGAを取得
$fuga = $tree->child('HOGE')->child('FUGA');
// 子ノードHOGEを追加して、それを削除(親ノードしか残ってない)
$tree->append('HOGE')->parent()->remove('HOGE')
// 「/」区切りのパス文字列を取得(/HOGE/FUGA)
echo implode('/', $tree->append('HOGE')->append('FUGA')->path());
/**
* 文字列をセパレータで分割したものをtree情報化する
*
* @param char $separator セパレータ文字
* @param string $string 文字列
* @return TreeNode TreeNode親要素
*/
public static function fromString($separator, $string)
{
// セパレータが特殊文字の場合はエスケープ
$sep = $separator;
if ($sep == '/' || $sep == '\\') {
$sep = '\\' . $sep;
}
// 先頭のセパレータを除去
$string = preg_replace('/^' . $sep . '/', '', $string);
$list = explode($separator, $string);
$tree = null;
foreach ($list as $str) {
if ($tree === null) {
$tree = new TreeNode($str);
} else {
$tree = $tree->append($str);
}
}
return $tree;
}
$tree = TreeNode('/', 'hoge/fuga');
var_dump($tree->path())
// array(2) { [0]=> string(4) "hoge" [1]=> string(4) "fuga" }
$tree = TreeNode('/', '/hoge/fuga');
var_dump($tree->path())
// array(3) { [0]=> string(0) "" [1]=> string(4) "hoge" [2]=> string(4) "fuga" }
/**
* 汎用ツリー構造クラス
*/
<?php
class TreeNode
{
/** 本オブジェクトの親要素 */
private $parent;
/** 本オブジェクトの子要素 */
private $children = [];
/** 名前 */
private $name;
/** 値 */
public $value;
/**
* コンストラクタ
*
* @param string $name 名前(親の場合は省略可能)
*/
public function __construct($name = null)
{
$this->name = $name;
}
/**
* 子を追加する
* すでに同名のものがある場合は追加しない
*
* @param string $name 追加する子要素
* @return TreeNode 追加した子要素
*/
public function append($name)
{
$child = $this->child($name);
if ($child === false) {
$child = new TreeNode($name);
$child->parent = $this;
$this->children[$child->name] = $child;
}
return $child;
}
/**
* 指定した子を削除する
*
* @param string $name 削除する子要素
* @return TreeNode 自分自身
*/
public function remove($name)
{
if ($this->has($name)) {
unset($this->children[$name]);
}
return $this;
}
/**
* 指定した名前の子要素を取得する
*
* @param string $name 名前(省略時は全部)
* @return TreeNode | array | boolean
*/
public function child($name = null)
{
// 引数省略時は全部
if (is_null($name)) {
return $this->children;
}
// 引数指定時は指定した名前の子要素
if (!$this->has($name)) {
return false;
}
return $this->children[$name];
}
/**
* 指定した名前の子要素があるかどうか
* 省略時は子要素自体があるかどうか
*
* @param string $name 名前
* @return boolean
*/
public function has($name = null)
{
// 引数省略時
if (is_null($name)) {
return (0 < count($this->children));
}
return isset($this->children[$name]);
}
/**
* 名前を取得
* @return string
*/
public function name()
{
return $this->name;
}
/**
* 親要素を取得
*
* @return TreeNode 親要素
*/
public function parent()
{
return $this->parent;
}
/**
* パスの配列を取得する
* valueは含まれません
* 例: [NULL, 'HOGE', 'FUGA']
*
* @return array
*/
public function path()
{
$result = [$this->name];
$parent = $this->parent;
while($parent != null) {
$result[] = $parent->name;
$parent = $parent->parent;
}
$result = array_reverse($result);
return $result;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment