I have a class with constants like this:
class AClass
{
const CODE_NAME_123 = "value1";
const CODE_NAME_456 = "value2";
}
Is there a way to convert the name of a constant like AClass::CODE_NAME_123 into a string in order to, e.g., extract the trailing digits from the string?
You can use something like this:
//PHP's ReflectionClass will allow us to get the details of a particular class
$r = new ReflectionClass('AClass');
$constants = $r->getConstants();
//all constants are now stored in an array called $constants
var_dump($constants);
//example showing how to get trailing digits from constant names
$digits = array();
foreach($constants as $constantName => $constantValue){
$exploded = explode("_", $constantName);
$digits[] = $exploded[2];
}
var_dump($digits);
You can use ReflectionClass::getConstants() and iterate through the result to find a constant with a specific value, and then get the last digits from the constant name with regex:
<?php
class AClass
{
const CODE_NAME_123 = "foo";
const CODE_NAME_456 = "bar";
}
function findConstantWithValue($class, $searchValue) {
$reflectionClass = new ReflectionClass($class);
foreach ($reflectionClass->getConstants() as $constant => $value) {
if ($value === $searchValue) {
return $constant;
}
}
return null;
}
function findConstantDigitsWithValue($class, $searchValue) {
$constant = findConstantWithValue($class, $searchValue);
if ($constant !== null && preg_match('/\d+$/', $constant, $matches)) {
return $matches[0];
}
return null;
}
var_dump( findConstantDigitsWithValue('AClass', 'foo') ); //string(3) "123"
var_dump( findConstantDigitsWithValue('AClass', 'bar') ); //string(3) "456"
var_dump( findConstantDigitsWithValue('AClass', 'nop') ); //NULL
?>
DEMO
Use ReflectionClass() and loop through the resulting keys applying a preg_match to each value to extract the last 3 numbers of the string:
class AClass
{
const CODE_NAME_123 = "";
const CODE_NAME_456 = "";
}
$constants = new ReflectionClass('AClass');
$constants_array = $constants->getConstants();
foreach ($constants_array as $constant_key => $constant_value) {
preg_match('/\d{3}$/i', $constant_key, $matches);
echo '<pre>';
print_r($matches);
echo '</pre>';
}
The output would be:
Array
(
[0] => 123
)
Array
(
[0] => 456
)
Related
In PHP we can do things like these:
Class Example {
...
}
$example = 'Example';
$object = new $example();
Or the use of variable variables:
$hour = 18;
$greets = array('Good morning','Good afternoon','Good evening');
$values = array(13,21,23);//people is sleeping at 23PM, so they don't greet.
$n = count($values);
$greet = 'greets';
for($i=0;$i<$n;$i++){
if($hour < $values[$i]){
echo 'hello, '.${$greet}[$i];
break;
}
}
And others..
I wonder if it would be possible to access directly to a specific index of a multidimensional array in a similar way. Something like:
$array = array(...); //multidimensional array.
$position = '[0][4][3]';
print_r($array$position);
Thanks in advance.
UPDATE
I'm so sorry because I finished my question in a wrong way.
I need to set the multimesional array and add a value. i.e:
$array$position = $data;
You could implement it yourself with a custom function:
function getValueFromMultiDimensionalArray( array $array, string $key )
{
$keys = explode('][', $key);
$value = $array;
foreach ($keys as $theKey) {
// remove the opening or closing bracket if present
$theKey = str_replace([ '[', ']' ], '', $theKey);
if (!isset($value[$theKey])) {
return null;
}
$value = $value[$theKey];
}
return $value;
}
You can define path as dot separated , check the following solution
function getValueByKey($a,$p){
$c = $a;
foreach(explode('.',$p) as $v){
if(!array_key_exists($v, $c)) return null;
$c = $c[$v];
}
return $c;
}
You can use this function as
$path = '1.2.3.0';
$indexValue = getValueByKey($array, $path);
Nope, this is not possible.
The only thing you can do is to implement ArrayAccess interface, which allows to access instances with [] operator. But you will have to define the logic yourself.
class MyClass implements ArrayAccess
{
...
}
$x = new MyClass([0=>[4=>[3=>'hello world']]]);
$position = '[0][4][3]';
echo $x[$position]; //hello world
Given a string that contains values separated by dots:
property.entry.item
What is the best way to convert that to a key for an associative array?
$result['imported_data']['property']['entry']['item']
The string may be of any length, with any number of dots and contain an value:
people.arizona.phoenix.smith
I've tried the following without success:
//found a dot, means we are expecting output from a previous function
if( preg_match('[.]',$value)) {
//check for previous function output
if(!is_null($result['import'])) {
$chained_result_array = explode('.',$value);
//make sure we have an array to work with
if(is_array($chained_result_array)) {
$array_key = '';
foreach($chained_result_array as $key) {
$array_key .= '[\''.$key.'\']';
}
}
die(print_r(${result.'[\'import\']'.$array_key}));
}
}
I was thinking I could convert the string to a variable variable, but I get an array to string conversion error.
You can explode the string into an array and loop through the array. (DEMO)
/**
* This is a test array
*/
$testArray['property']['entry']['item'] = 'Hello World';
/**
* This is the path
*/
$string = 'property.entry.item';
/**
* This is the function
*/
$array = explode('.', $string);
foreach($array as $i){
if(!isset($tmp)){
$tmp = &$testArray[$i];
} else {
$tmp = $tmp[$i];
}
}
var_dump( $tmp ); // output = Hello World
Split the string into parts, and itterate the array, accessing each element in turn:
function arrayDotNotation($array, $dotString){
foreach(explode('.', $dotString) as $section){
$array = $array[$section];
}
return $array;
}
$array = ['one'=>['two'=>['three'=>'hello']]];
$string = 'one.two.three';
echo arrayDotNotation($array, $string); //outputs hello
Live example: http://codepad.viper-7.com/Vu8Hhy
You should really check to see if keys exist before you reference them. Otherwise, you're going to spew a lot of warnings.
function getProp($array, $propname) {
foreach(explode('.', $propname) as $node) {
if(isset($array[$node]))
$array = &$array[$node];
else
return null;
}
return $array;
}
Now you can do things like:
$x = array(
'name' => array(
'first' => 'Joe',
'last' => 'Bloe',
),
'age' => 27,
'employer' => array(
'current' => array(
'name' => 'Some Company',
)
)
);
assert(getProp($x, 'age') == 27);
assert(getProp($x, 'name.first') == 'Joe');
assert(getProp($x, 'employer.current.name') == 'Some Company');
assert(getProp($x, 'badthing') === NULL);
assert(getProp($x, 'address.zip') === NULL);
Or, if you are only interested in the import section of the tree:
getProp($x['import'], 'some.path');
I'v recently came accross a problem with some code as shown below:
$key = "upload_8_fid_aids.tmp";
public function to_key($key) {
$s = $this->table;//$s = kv
foreach((array)$key as $k=>$v) {
$s .= '-'.$this->primarykey[$k].'-'.$v;
}
return $s;
}
There's a (array)$key signature out there in the foreach loop,the first thing I'm stucking in is the "array" that prefixed with the variabls $k,what does this mean?The very first idea that hit upon me is that it converts the $k to an array,though,the variable $k is a string,is it plausible to convert string to array in php?I think it is unreasonable.So what does that array mean?
Thanks in advance!
When you cast a string to an array in PHP it becomes an array with the string pushed to it.
Example:
$test = "This is a string!";
print_r((array) $test);
Output:
Array
(
[0] => This is a string!
)
That said I find the code strange, I don't see the need for the loop, it could just be:
$key = "upload_8_fid_aids.tmp";
public function to_key($key) {
$s = $this->table; //$s = kv
$s .= '-' . $this->primarykey[0] . '-' . $key;
return $s;
}
Any type enclosed in parentheses is telling PHP to cast the following thing to that type.
In this case, it's a cheap way to avoid having to check if( is_array($key)), by just forcing it to be one.
Converting an object to an array:
<?php
/*** create an object ***/
$obj = new stdClass;
$obj->foo = 'foo';
$obj->bar = 'bar';
$obj->baz = 'baz';
/*** cast the object ***/
$array = (array) $obj;
/*** show the results ***/
print_r( $array );
?>
Result:
Array
(
[foo] => foo
[bar] => bar
[baz] => baz
)
I have some logic that is being used to sort data but depending on the user input the data is grouped differently. Right now I have five different functions that contain the same logic but different groupings. Is there a way to combine these functions and dynamically set a value that will group properly. Within the function these assignments are happening
For example, sometimes I store the calculations simply by:
$calcs[$meter['UnitType']['name']] = ...
but other times need a more specific grouping:
$calcs[$meter['Resource']['name']][$meter['UnitType']['name']][date('Y-m',$start)] =...
As you can see sometimes it is stored in a multidiminesional array and other times not. I have been trying to use eval() but without success (not sure that is the correct approach). Storing the data in a temporary variable does not really save much because there are many nested loops and if statements so the array would have to be repeated in multiple places.
EDIT
I hope the following example explains my problem better. It is obviously a dumbed down version:
if(){
$calcs[$meter['UnitType']['name']] = $data;
} else {
while () {
$calcs[$meter['UnitType']['name']] = $data;
}
}
Now the same logic can be used but for storing it in different keys:
if(){
$calcs[$meter['Resource']['name']][$meter['UnitType']['name']][date('Y-m',$start)] = $data;
} else {
while () {
$calcs[$meter['Resource']['name']][$meter['UnitType']['name']][date('Y-m',$start)] = $data;
}
}
Is there a way to abstract out the keys in the $calc[] array so that I can have one function instead of having multiple functions with different array keys?
You can use this if you want to get&set array values dynamically.
function getVal($data,$chain){
$level = $data;
for($i=0;$i<count($chain);$i++){
if(isset($level[$chain[$i]]))
$level = $level[$chain[$i]];
else
return null; // key does not exist, return null
}
return $level;
}
function setVal(&$data,$chain,$value){
$level = &$data;
for($i=0;$i<count($chain);$i++){
$level = &$level[$chain[$i]]; // set reference (&) in order to change the value of the object
}
$level = $value;
}
How it works:
Calling getVal($data,array('foo','bar','2017-08')) will return the equivalent of $data['foo']['bar']['2017-08'].
Calling setVal($data,array('foo','bar','2017-08'),'hello') will set value as if you called
$data['foo']['bar']['2017-08'] = 'hello'. non-existent keys will be created automatically by php magic.
This can be useful if you want to build the structure of the array dynamically.
Here's a function I wrote for setting deeply nested members on arrays or objects:
function dict_set($var, $path, $val) {
if(empty($var))
$var = is_array($var) ? array() : new stdClass();
$parts = explode('.', $path);
$ptr =& $var;
if(is_array($parts))
foreach($parts as $part) {
if('[]' == $part) {
if(is_array($ptr))
$ptr =& $ptr[];
} elseif(is_array($ptr)) {
if(!isset($ptr[$part]))
$ptr[$part] = array();
$ptr =& $ptr[$part];
} elseif(is_object($ptr)) {
if(!isset($ptr->$part))
$ptr->$part = array();
$ptr =& $ptr->$part;
}
}
$ptr = $val;
return $var;
}
Using your example data:
$array = [];
$array = dict_set($array, 'resource1.unit1.2017-10', 'value1');
$array = dict_set($array, 'resource1.unit2.2017-11', 'value2');
$array = dict_set($array, 'resource2.unit1.2017-10', 'value3');
print_r($array);
Results in output like:
Array
(
[resource1] => Array
(
[unit1] => Array
(
[2017-10] => value1
)
[unit2] => Array
(
[2017-11] => value2
)
)
[resource2] => Array
(
[unit1] => Array
(
[2017-10] => value3
)
)
)
The second argument to dict_set() is a $path string in dot-notation. You can build this using dynamic keys with period delimiters between the parts. The function works with arrays and objects.
It can also append incremental members to deeply nested array by using [] as an element of the $path. For instance: parent.child.child.[]
Would it not be easier to do the following
$calcs = array(
$meter['Resource']['name'] => array(
$meter['UnitType']['name'] => 'Some Value',
$meter['UnitType']['name2'] => 'Some Value Again'
),
);
or you can use Objects
$calcs = new stdClass();
$calcs->{$meter['UnitType']['name']} = 'Some Value';
but I would advice you build your structure in arrays and then do!
$calcs = (object)$calcs_array;
or you can loop your first array into a new array!
$new = array();
$d = date('Y-m',$start);
foreach($meter as $key => $value)
{
$new[$key]['name'][$d] = array();
}
Give it ago and see how the array structure comes out.
Try to use a switch case.
<?php
$userinput = $calcs[$meter['UnitType']['name']] = $data;;
switch ($userinput) {
case "useriput1":
while () {
$calcs[$meter['Resource']['name']][$meter['UnitType']['name']][date('Y-m',$start)] = $data;
}
break;
case "userinput2":
while () {
$calcs[$meter['Resource']['name']][$meter['UnitType']['name']][date('Y-m',$start)] = $data;
}
break;
...
default:
while () {
$calcs[$meter['Resource']['name']][$meter['UnitType']['name']][date('Y-m',$start)] = $data;
}
}
?>
I agree with the comment on the OP by #Jake N that perhaps using objects is a better approach. Nonetheless, if you want to use arrays, you can check for the existence of keys in a conditional, like so:
if(
array_key_exists('Resource', $meter)
) {
$calcs[$meter['Resource']['name']][$meter['UnitType']['name']][date('Y-m',$start)] = $data;
} else {
$calcs[$meter['UnitType']['name']] = $data;
}
On the other hand, if you want to use objects, you can create a MeterReading object type, and then add MeterReading instances as array elements to your $calcs array, like so:
// Object defintion
class MeterReading {
private $data;
private $resource;
private $startDate;
private $unitType;
public function __construct(Array $meter, $start, $data) {
$this->unitType = $meter['UnitType']['name'];
$this->resource = $meter['Resource']['name'];
$this->startDate = date('Y-m',$start);
}
public function data() {
return $this->data;
}
public function resource() {
return $this->resource;
}
public function startDate() {
return $this->startDate;
}
public function unitType() {
return $this->unitType;
}
}
// Example population
$calcs[] = new MeterReading($meter, $start, $data);
// Example usage
foreach($calcs as $calc) {
if($calc->resource()) {
echo 'Resource: ' . $calc->resource() . '<br>';
}
echo 'Unit Type: ' . $calc->unitType() . '<br>';
echo 'Start Date: ' . $calc->startDate() . '<br>';
echo 'Data: ' . $calc->data() . '<br>';
}
Obviously you can take this further, such as checking the existence of array keys in the object constructor, giving the object property resource a default value if not provided, and so on, but this is a start to an OO approach.
You can use this library to get or set value in multidimensional array using array of keys:
Arr::getNestedElement($calcs, [
$meter['Resource']['name'],
$meter['UnitType']['name'],
date('Y-m', $start)
]);
to get value or:
Arr::handleNestedElement($calcs, [
$meter['Resource']['name'],
$meter['UnitType']['name'],
date('Y-m', $start)
], $data);
to set $data as value.
I'm building a platform. Somewhere in my code, there's an array that looks like this (PHP):
$entries = array('p01','p02','g01','g02','a001','a002')
I need to write a script that filters the array based on the first letter. For example, asking for those with the starting letter "p" would give me
$filtered_entries = array('p01','p02');
Similarly, if I asked for those with starting letter "g" or "a" it would give me those as well. Any idea how to accomplish this?
There is an array_filter() function in PHP which you can use to accomplish this:
$filtered = array_filter($array, create_function('$a', 'return $a[0] == "' . $letter . '";'));
I'll leave it to you to generalize the function to handle all the letters.
See: http://www.php.net/manual/en/function.array-filter.php
class FirstCharFilter {
public $char = 'p';
function filter(array $array){
return array_filter($array,array($this,'checkFirstChar'));
}
public function checkFirstChar($a){
return $a[0] == $this->char;
}
}
$filter = new FirstCharFilter();
$filter->char = 'p';
var_dump($filter->filter($array));
$filter->char = 'g';
var_dump($filter->filter($array));
Or if you only need to loop, extend FilterIterator:
class FirstCharIterator extends FilterIterator {
public $char = '';
function accept(){
$string = $this->current();
return is_string($string) && $string[0] == $this->char;
}
}
$iter = new FirstCharIterator(new ArrayIterator($array));
$iter->char = 'p';
foreach($iter as $item) echo $item."\n";
$entries = array('p01','p02','g01','g02','a001','a002');
print_r(
preg_grep('~^p~', $entries) // or preg_grep("~^$letter~",.....
);
http://php.net/manual/en/function.preg-grep.php
function filter_array($array, $letter){
$filtered_array=array();
foreach($array as $key=>$val){
if($val[0]==$letter){
$filtered_array[]=$val;
}
}
return $filtered_array;
}
use it like this to get all p's
$entries = array('p01','p02','g01','g02','a001','a002')
$filtered=filter_array($entries, 'p');
$entries = array('p01','p02','g01','g02','a001','a002');
$filterVar = null;
function filterFunction($v) {
global $filterVar;
if (substr($v,0,1) == $filterVar) {
return $v;
}
}
$filterVar = 'a';
$newEntries = array_filter($entries,'filterFunction');
var_dump($newEntries);
Here's one way of generating filter functions using a closure.
function filter_factory($letter) {
return function ($input) use ($letter) {
return is_string($input) && $input[0] === $letter;
};
}
$entries = array('p01','p02','g01','g02','a001','a002');
$p_entries = array_filter($entries, filter_factory('p'));
This type of solution is much more intuitive and dynamic.
In this example, there are several types of solutions:
Search in the first letters
Sensitive to capital letters
is_array() so if it tends to avoid several errors
<?php
/*
* Search within an asociative array
* Examples:
* $array = array('1_p01','1_P02','2_g01','2_g02','3_a001','3_a002');
* find_in_array($array,'2');
* return: array( 2 => '2_g01',3 => '2_g02')
*
* find_in_array($array,'2',false);
* return: array( 1 => '1_P02')
*
* find_in_array($array,'P0',false,false);
* return: array( 0 => '1_p01',1 => '1_P02')
*
*/
function find_in_array($array, $find='', $FirstChar=true, $CaseInsensitive=true){
if ( is_array($array) ){
return preg_grep("/".($FirstChar ? '^':'')."{$find}/".($CaseInsensitive ? '':'i'), $array);
}
}
$array = array('1_p01','1_P02','2_g01','2_g02','3_a001','3_a002');
$a = find_in_array($array,'2');
var_export($a);
/*
Return:
array (
2 => '2_g01',
3 => '2_g02'
)
*/
$a = find_in_array($array,'P0',false);
var_export($a);
/*
Return:
array (
1 => '1_P02'
)
*/
$a = find_in_array($array,'P0',false,false);
var_export($a);
/*
Return:
array (
0 => '1_p01',
1 => '1_P02'
)
*/