php get method arguments default values - php

I want to get list of methods inside a class as well as their arguments and default values. how can I do that? below is the code that I used:
$class = new ReflectionClass($className);
$methods = [];
foreach($class->getMethods() as $method){
if($method->class == $className && $method->name != '__construct' ){
$obj = [];
$obj['controller'] = $className;
$obj['action'] = $method->name;
$obj['params'] = array_map(function($value){return $value->name;}, $method->getParameters());
$methods[] = $obj;
}
}
The sample result of above code is like:
Array(
[0] => Array
(
[controller] => Controller,
[action] => function,
[params] => Array
(
[0] => offset,
[1] => limit
)
)
)
How can I get function arguments default values?

In your array_map function for the parameters, you can insert a check whether the parameter has a default value using ->isDefaultValueAvailable() and if so - list it using ->getDefaultValue(). See the example below based on your code and change it according to your needs.
Instead of
$obj['params'] = array_map(
function($value){return $value->name;},
$method->getParameters()
);
Use
$obj['params'] = array_map(
function($value){
return $value->name.
($value->isDefaultValueAvailable() ? '='.$value->getDefaultValue() : '');
},
$method->getParameters()
);

Related

How to get variable name and value in AST

I'm using PHP-PArser to find the AST of PHP program. For example:
code
<?php
use PhpParser\Error;
use PhpParser\NodeDumper;
use PhpParser\ParserFactory;
$code = <<<'CODE'
<?php
$variable = $_POST['first'];
$new = $nonexist;
CODE;
$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
try {
$ast = $parser->parse($code);
} catch (Error $error) {
echo "Parse error: {$error->getMessage()}\n";
return;
}
$dumper = new NodeDumper;
echo $dumper->dump($ast) . "\n";
The AST result of the above example as following:
array( 0: Stmt_Expression( expr: Expr_Assign( var: Expr_Variable( name: variable ) expr: Expr_ArrayDimFetch( var: Expr_Variable( name: _POST_first_symbol ) dim: Scalar_String( value: first ) ) ) ) 1: Stmt_Expression( expr: Expr_Assign( var: Expr_Variable( name: new ) expr: Expr_Variable( name: nonexist ) ) ) )
What I'm trying to find is the variable = _POST AND new = nonexist
I used leavenode function to reach _POST and variable. my code to find _POSTand variable as following:
public function leaveNode(Node $node)
{
$collect_to_print= array();
if ($node instanceof ArrayDimFetch
&& $node->var instanceof Variable
&& $node->var->name === '_POST')
{
$variableName = (string) $node->var->name;
$collect_to_print[$node->dim->value] = $node->var->name; // will store the variables in array in a way to print them all later such as variable => _POST , how to get the name `variable` in this case
return $node;
}
else
if ($node instanceof Variable
&& !($node->var->name === '_POST' ))
{
$collect_to_print[$node->name] = 'Empty' ;
}
}
My results until now show every variable in separate line as following:
variable =>
first => _POST // This _POST should be the value of variable (above)
new => Empty
nonexist => Empty
However, I expect the result to be:
variable => _POST
new => Empty
nonexist => Empty
any help please
This is a lot more complicated than other questions you've asked, but it has been interesting to learn about how to write it.
I've put comments through the code, but basically it analyses the code and looks for assignments (instances of PhpParser\Node\Expr\Assign nodes). It then splits it into left and right parts and recursively extracts any variables in either parts.
The code allows for nested variables on either side of the expression, I've changed the example code to provide some broader examples.
Comments in code (assumes some knowledge of how the parser works with nodes etc.)...
$traverser = new NodeTraverser;
class ExtractVars extends NodeVisitorAbstract {
private $prettyPrinter = null;
private $variables = [];
private $expressions = [];
public function __construct() {
$this->prettyPrinter = new PhpParser\PrettyPrinter\Standard;
}
public function leaveNode(Node $node) {
if ( $node instanceof PhpParser\Node\Expr\Assign ) {
$assignVars = $this->extractVarRefs ( $node->var );
// Get string of what assigned to actually is
$assign = $this->prettyPrinter->prettyPrintExpr($node->var);
// Store the variables associated with the left hand side
$this->expressions[$assign]["to"] = $assignVars;
// Store variables from right
$this->expressions[$assign][] = $this->extractVarRefs ( $node->expr );
}
}
private function extractVarRefs ( Node $node ) : array {
$variableList = [];
// If it's a variable, store the name
if ( $node instanceof PhpParser\Node\Expr\Variable ) {
$variable = $this->prettyPrinter->prettyPrintExpr($node);
$this->variables[] = $variable;
$variableList[] = $variable;
}
// Look for any further variables in the node
foreach ( $node->getSubNodeNames() as $newNodeName ) {
$newNode = $node->$newNodeName;
if ( $newNode instanceof Node && $newNode->getSubNodeNames()) {
// Recursive call to extract variables
$toAdd = $this->extractVarRefs ( $newNode );
// Add new list to current list
$variableList = array_merge($variableList, $toAdd);
}
}
return $variableList;
}
public function getVariables() : array {
return array_unique($this->variables);
}
public function getExpressions() : array {
return $this->expressions;
}
}
$varExtract = new ExtractVars();
$traverser->addVisitor ($varExtract);
$traverser->traverse($ast);
print_r($varExtract->getVariables());
print_r($varExtract->getExpressions());
Which gives the list of variables as...
Array
(
[0] => $_POST
[1] => $b
[3] => $new
[4] => $nonexist
)
And the list of expressions as
Array
(
[$_POST[$b]] => Array
(
[to] => Array
(
[0] => $_POST
[1] => $b
)
[0] => Array
(
[0] => $_POST
)
)
[$new] => Array
(
[to] => Array
(
[0] => $new
)
[0] => Array
(
[0] => $nonexist
)
[1] => Array
(
[0] => $_POST
[1] => $b
)
)
)
note that the [to] element of the array contains any variables involved on the left of the =.

Is there an elegant way to reduce an structure to a simple array?

This is a typical array structure:
$s = array ('etc'=>'etc', 'fields' =>
array (
0 => array (
'name'=>'year', 'description'=>'Year of ...', 'type'=>'integer',
),
1 => array (
'name'=>'label', 'description'=>'Offical short name', type'=>'string',
),
2 => array (
'name' => 'xx', 'description' => 'Xx ...', 'type' => 'string',
)
));
Here is a non-elegant way (or "not so elegant way") to reduce the big array to a simple array containing just one column:
$fields = array();
foreach ($strut['resources'][0]['schema']['fields'] as $r)
$fields[] = $r['name'];
This works, but is it possible to do the same with only one instruction? Perhaps using like array_reduce(), but I not see how.
Here are other typical "elegance PHP problem":
$fieldsByName = array();
foreach ($strut['resources'][0]['schema']['fields'] as $r)
$fields[$r['name']] = array(
'description' =>$r['description'],
'type' =>$r['type']
);
Is there a PHP alternative? The idea here is to use the keyword (name in the example) as an array key, and the other elements as usual fields, so, the generic non-elegant algorithm is
$fieldsByName = array();
foreach ($strut['resources'][0]['schema']['fields'] as $r){
$key = $r['name'];
unset($r['name']);
$fields[$key] = $r;
}
You can use array_column to extract all values with key name into another array
$names = array_column($strut['resources'][0]['schema']['fields'], 'name');
you could put your array thru this function:
function flatten(array $array) {
$return = array();
array_walk_recursive($array, function($a) use (&$return) { $return[] = $a; });
return $return;
}
it will result in just a literal sequence of just values of your multidimensional array, like so.
Array
(
[0] => etc
[1] => year
[2] => Year of ...
[3] => integer
[4] => day
[5] => Day of the ...
[6] => string
[7] => xx
[8] => Xx ...
[9] => string
)
then, as you know original structure - you can parse this how needed. 4ex: every third value could be new assoc array's key value that holds an array with arrays of first two values.., or as you wish
array_column is first logical announcement, no surprises there.
Depending on how normalized your data is and how often this issues comes up, you could implement a class around your data. You can use the ArrayAccess, Iterator and Countable to make the change completely transparent, and you would be able to implement helper methods to hide the complexity of fetching data.
Here is an example, just using ArrayAccess:
class Strut implements ArrayAccess {
private $data;
private $fieldsByName = null;
public function __construct($data) {
$this->data = $data;
}
public function fieldsByName() {
//If the result has not already been computed
if($this->fieldsByName === null) {
$this->fieldsByName = array();
foreach($this->data['resources'][0]['schema']['fields'] as $r) {
$this->fieldsByName[ $r['name'] ] = array(
'description' =>$r['description'],
'type' =>$r['type']
);
}
}
return $this->fieldsByName;
}
/**
* ArrayAccess Methods
*/
public function offsetSet($offset, $value) {
$this->data[$offset] = $value;
}
public function offsetExists($offset) {
return isset( $this->data[$offset] );
}
public function offsetUnset($offset) {
unset( $this->data[$offset] );
}
public function offsetGet($offset) {
return isset( $this->data[$offset] ) ? $this->data[$offset] : null;
}
}
Using the above code you should be able to access your data just has you have been, but you also have the option of defining additional accessors in a nice container. Note that you also have to implement the Iterator interface to be able to foreach over your data.
This doesn't address the "elegance" issue of the underlying implementation (the other solutions do a nice job of that), but this way hides the complexity completely.

Removing object from array; how to avoid nulls

I have an array that is a object which I carry in session lifeFleetSelectedTrucksList
I also have objects of class fleetUnit
class fleetUnit {
public $idgps_unit = null;
public $serial = null;
}
class lifeFleetSelectedTrucksList {
public $arrayList = array();
}
$listOfTrucks = new lifeFleetSelectedTrucksList(); //this is the array that I carry in session
if (!isset($_SESSION['lifeFleetSelectedTrucksList'])) {
$_SESSION['lifeFleetSelectedTrucksList'] == null; //null the session and add new list to it.
} else {
$listOfTrucks = $_SESSION['lifeFleetSelectedTrucksList'];
}
I use this to remove element from array:
$listOfTrucks = removeElement($listOfTrucks, $serial);
And this is my function that removes the element and returns the array without the element:
function removeElement($listOfTrucks, $remove) {
for ($i = 0; $i < count($listOfTrucks->arrayList); $i++) {
$unit = new fleetUnit();
$unit = $listOfTrucks->arrayList[$i];
if ($unit->serial == $remove) {
unset($listOfTrucks->arrayList[$i]);
break;
} elseif ($unit->serial == '') {
unset($listOfTrucks->arrayList[$i]);
}
}
return $listOfTrucks;
}
Well, it works- element gets removed, but I have array that has bunch of null vaues instead. How do I return the array that contains no null elements? Seems that I am not suing something right.
I think what you mean is that the array keys are not continuous anymore. An array does not have "null values" in PHP, unless you set a value to null.
$array = array('foo', 'bar', 'baz');
// array(0 => 'foo', 1 => 'bar', 2 => 'baz');
unset($array[1]);
// array(0 => 'foo', 2 => 'baz');
Two approaches to this:
Loop over the array using foreach, not a "manual" for loop, then it won't matter what the keys are.
Reset the keys with array_values.
Also, removing trucks from the list should really be a method of $listOfTrucks, like $listOfTrucks->remove($remove). You're already using objects, use them to their full potential!
You can use array_filter
<?php
$entry = array(
0 => 'foo',
1 => false,
2 => -1,
3 => null,
4 => ''
);
print_r(array_filter($entry));
?>
output:
Array
(
[0] => foo
[2] => -1
)

Extended addslashes function in PHP

Hi can somebody help me with building an extended addslashes function, which will work with mixed combination of objects and arrays. For example i have this Object:
$object = new stdClass;
$object2 = new stdClass;
$object2->one = "st'r2";
$object3 = new stdClass;
$object3->one = "st'r3";
$object->one = "s'tr";
$object->two = array($object2);
$object->obj = $object3;
And i would like to get this object back escaped and with the same structure.
I have started some experiments and i get something like this:
function addslashes_extended($arr_r){
if(is_array($arr_r)){
foreach ($arr_r as $key => $val){
is_array($val) ? addslashes_extended($val):$arr_r[$key]=addslashes($val);
}
unset($val);
}else if(is_object($arr_r)){
$objectProperties = get_object_vars($arr_r);
foreach($objectProperties as $key => $value){
is_object($value) ? addslashes_extended($value):$arr_r->{$key}=addslashes($value);
}
}
return $arr_r;
}
But this is not going to work, i have to work with passing by reference i think, but i have no clue how, other solutions would be nice to have too, thanks in advance!
Try this (using array_walk):
error_reporting(E_ALL ^ E_STRICT);
ini_set('display_errors', 'on');
$data = array(
"fo'o",
'bar' => "foo'bar",
'foobar' => array(
1, 2, 'someObj' => json_decode('{"prop1": "a", "prop2": "b\'c"}')
)
);
class Util
{
public static function addslashes_extended(&$mixed) {
if (is_array($mixed) || is_object($mixed)) {
array_walk($mixed, 'Util::addslashes_extended');
}
elseif (is_string($mixed)) {
$mixed = addslashes($mixed);
}
}
}
Util::addslashes_extended($data);
print_r($data);
Output ( http://codepad.org/nUUYKWrn ):
Array
(
[0] => fo\'o
[bar] => foo\'bar
[foobar] => Array
(
[0] => 1
[1] => 2
[someObj] => stdClass Object
(
[prop1] => a
[prop2] => b\'c
)
)
)
To pass by reference, you use & before the variable name, quick example:
function inc(&$var) {
$var++;
}
$x = 5;
inc($x);
echo $x; //Prints: 6

PHP providing a closure to determine uniqueness of an item in an array

Quick question: is there a way to provide a closure in PHP to some equivalent function to the array_unique function so that you can specify your own comparison closure to be used when comparing two items in the array? I have an array of class instances (which may contain duplicates) and want to tell PHP to use particular logic to determine uniqueness.
PHP provides this with sorting using the usort() method - just wondering if it is also available for uniqueness checks. Thanks!
there is array_filter that you can apply a callback to each element in an array and return true/false of whether to keep that value in the returning array. Here is a comment using array_filter to remove duplicates in an array.
I couldn't find exactly what you are looking for but i thought maybe it wouldn't be too tough to write your own function...
$a = new StdClass;
$b = new StdClass;
$c = new StdClass;
$d = new StdClass;
$a->a = 1;
$b->a = 1;
$c->c = 1;
$d->c = 1;
$objects = array( $a,$b,$c,$d );
function custom_array_unique( array $objects ) {
foreach( $objects as $k =>$object ) {
foreach( $objects as $k2 => $object2 ) {
if ( $k !== $k2 && $object == $object2 ) {
unset( $objects[$k] );
}
}
}
return $objects;
}
print_r( custom_array_unique($objects));
Array
(
[1] => stdClass Object
(
[a] => 1
)
[3] => stdClass Object
(
[c] => 1
)
)
The manual page for array_unique() doesn't provide any links to a callback version, and there isn't a function in the list of links on the left called array_uunique() (which is what such a function should be called if it follows the naming convention of the other array sorting functions - but then PHP isn't very reliable when it comes to function naming conventions).
You could add this functionality yourself using a double foreach loop:
$uniqueness_fails = false;
foreach ( $myarray as $keyA => $valueA ) {
foreach ( $myarray as $keyB => $valueB ) {
if ( $keyA != $keyB and my_equality_function($valueA, $valueB) ) {
$uniqueness_fails = true;
break 2;
}
}
}

Categories