Page Menu
Home
WickedGov Phorge
Search
Configure Global Search
Log In
Files
F1430349
Traverser.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
6 KB
Referenced Files
None
Subscribers
None
Traverser.php
View Options
<?php
/**
* This file is part of the Peast package
*
* (c) Marco Marchiò <marco.mm89@gmail.com>
*
* For the full copyright and license information refer to the LICENSE file
* distributed with this source code
*/
namespace
Peast
;
/**
* Nodes traverser class
*
* @author Marco Marchiò <marco.mm89@gmail.com>
*/
class
Traverser
{
/**
* If a function return this value, the current node will be removed
*/
const
REMOVE_NODE
=
1
;
/**
* If a function return this value, the current node's children won't be
* traversed
*/
const
DONT_TRAVERSE_CHILD_NODES
=
2
;
/**
* If a function return this value, the traverser will stop running
*/
const
STOP_TRAVERSING
=
4
;
/**
* Array of functions to call on each node
*
* @var array
*/
protected
$functions
=
array
();
/**
* Pass parent node flag
*
* @var bool
*/
protected
$passParentNode
=
false
;
/**
* Skip starting node flag
*
* @var bool
*/
protected
$skipStartingNode
=
false
;
/**
* Class constructor. Available options are:
* - skipStartingNode: if true the starting node will be skipped
* - passParentNode: if true the parent node of each node will be
* passed as second argument when the functions are called. Note
* that the parent node is calculated during traversing, so for
* the starting node it will always be null.
*
* @param array $options Options array
*/
public
function
__construct
(
$options
=
array
())
{
if
(
isset
(
$options
[
"passParentNode"
]))
{
$this
->
passParentNode
=
(
bool
)
$options
[
"passParentNode"
];
}
if
(
isset
(
$options
[
"skipStartingNode"
]))
{
$this
->
skipStartingNode
=
(
bool
)
$options
[
"skipStartingNode"
];
}
}
/**
* Adds a function that will be called for each node in the tree. The
* function will receive the current node as argument. The action that will
* be executed on the node by the traverser depends on the returned value
* of the function:
* - a node: it will replace the node with the returned one
* - a numeric value that is a combination of the constants defined in this
* class: it will execute the function related to each constant
* - an array where the first element is a node and the second element is a
* numeric value that is a combination of the constants defined in this
* class: it will replace the node with the returned one and it will
* execute the function related to each constant (REMOVE_NODE will be
* ignored since it does not make any sense in this case)
* - other: nothing
*
* @param callable $fn Function to add
*
* @return $this
*/
public
function
addFunction
(
callable
$fn
)
{
$this
->
functions
[]
=
$fn
;
return
$this
;
}
/**
* Starts the traversing
*
* @param Syntax\Node\Node $node Starting node
*
* @return Syntax\Node\Node
*/
public
function
traverse
(
Syntax\Node\Node
$node
)
{
if
(
$this
->
skipStartingNode
)
{
$this
->
traverseChildren
(
$node
);
}
else
{
$this
->
execFunctions
(
$node
);
}
return
$node
;
}
/**
* Executes all functions on the given node and, if required, starts
* traversing its children. The returned value is an array where the first
* value is the node or null if it has been removed and the second value is
* a boolean indicating if the traverser must continue the traversing or not
*
* @param Syntax\Node\Node $node Node
* @param Syntax\Node\Node|null $parent Parent node
*
* @return array
*/
protected
function
execFunctions
(
$node
,
$parent
=
null
)
{
$traverseChildren
=
true
;
$continueTraversing
=
true
;
foreach
(
$this
->
functions
as
$fn
)
{
$ret
=
$this
->
passParentNode
?
$fn
(
$node
,
$parent
)
:
$fn
(
$node
);
if
(
$ret
)
{
if
(
is_array
(
$ret
)
&&
$ret
[
0
]
instanceof
Syntax\Node\Node
)
{
$node
=
$ret
[
0
];
if
(
isset
(
$ret
[
1
])
&&
is_numeric
(
$ret
[
1
]))
{
if
(
$ret
[
1
]
&
self
::
DONT_TRAVERSE_CHILD_NODES
)
{
$traverseChildren
=
false
;
}
if
(
$ret
[
1
]
&
self
::
STOP_TRAVERSING
)
{
$continueTraversing
=
false
;
}
}
}
elseif
(
$ret
instanceof
Syntax\Node\Node
)
{
$node
=
$ret
;
}
elseif
(
is_numeric
(
$ret
))
{
if
(
$ret
&
self
::
DONT_TRAVERSE_CHILD_NODES
)
{
$traverseChildren
=
false
;
}
if
(
$ret
&
self
::
STOP_TRAVERSING
)
{
$continueTraversing
=
false
;
}
if
(
$ret
&
self
::
REMOVE_NODE
)
{
$node
=
null
;
$traverseChildren
=
false
;
break
;
}
}
}
}
if
(
$traverseChildren
&&
$continueTraversing
)
{
$continueTraversing
=
$this
->
traverseChildren
(
$node
);
}
return
array
(
$node
,
$continueTraversing
);
}
/**
* Traverses node children. It returns a boolean indicating if the
* traversing must continue or not
*
* @param Syntax\Node\Node $node Node
*
* @return bool
*/
protected
function
traverseChildren
(
Syntax\Node\Node
$node
)
{
$continue
=
true
;
foreach
(
Syntax\Utils
::
getNodeProperties
(
$node
,
true
)
as
$prop
)
{
$getter
=
$prop
[
"getter"
];
$setter
=
$prop
[
"setter"
];
$child
=
$node
->
$getter
();
if
(!
$child
)
{
continue
;
}
elseif
(
is_array
(
$child
))
{
$newChildren
=
array
();
foreach
(
$child
as
$c
)
{
if
(!
$c
||
!
$continue
)
{
$newChildren
[]
=
$c
;
}
else
{
list
(
$c
,
$continue
)
=
$this
->
execFunctions
(
$c
,
$node
);
if
(
$c
)
{
$newChildren
[]
=
$c
;
}
}
}
$node
->
$setter
(
$newChildren
);
}
else
{
list
(
$child
,
$continue
)
=
$this
->
execFunctions
(
$child
,
$node
);
$node
->
$setter
(
$child
);
}
if
(!
$continue
)
{
break
;
}
}
return
$continue
;
}
}
File Metadata
Details
Attached
Mime Type
text/x-php
Expires
Sat, May 16, 18:39 (5 h, 47 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
ca/dd/d588c7d5e1c6a9fa592f8ade79b1
Default Alt Text
Traverser.php (6 KB)
Attached To
Mode
rMWPROD MediaWiki Production
Attached
Detach File
Event Timeline
Log In to Comment