Call function again if array - php

I got a router in my MVC, it works fine but I think it can be shortened twice because anything beside of if (is_array($path)) and after else is the same
Currently I just copied code when condition is_array returns true and use foreach to loop through $path. When condition is_array is false it just use else statement.
public function run()
{
// Get string of query
$uri = $this->getURI();
// Check availability of query in routes.php
foreach ($this->routes as $uriPattern => $path) {
// Compare $uriPattern and $uri
if (preg_match("~$uriPattern~", $uri)) {
if (is_array($path)) {
foreach ($path as $p) {
//***logic here***
}
} else {
//***same logic here but for a string***
}
Currently my code is bigger than it should because 50% of it just a copy af itself. Can you suggest what would be a more elegant way to split array and loop though paths? I thought about recursion but kinda don't know how to apply it in this case.
Thanks

public function run()
{
$uri = $this->getURI();
foreach ($this->routes as $uriPattern => $path) {
if (preg_match("~$uriPattern~", $uri)) {
if (!is_array($path)) {
$path = [$path];
}
foreach ($path as $p) {
do_logic($p);
}
}
}
}

Related

While loop on multi-dimensional array

So I have a function that currently has a foreach and it works amazing, but I'm being forced to change it to a while loop:
PLEASE NOTE: The developers at my company don't want to use the foreach and they think that a while loop would be more efficient, but I'm not understanding how that would be executed, so I need some help.
So I have the following function ($post_blocks is an array of arrays):
public function parse_block_data(string $block_name, string $selector, $post_id)
{
if (!has_blocks($post_id)) {
return false;
}
$post_blocks = parse_blocks(get_the_content('', false, $post_id));
foreach ($post_blocks as $block) {
if ($block_name != $block['blockName']) {
continue;
}
if (!isset($block['attrs']['id'])) {
return false;
}
if (isset($block['attrs']['data'][$selector])) {
return $block['attrs']['data'][$selector];
} else {
break;
}
}
return false;
}
It uses the parameters to build up an array as shown below:
Output
So I started building a while loop inside the function, but I'm clueless on how to achieve it without using a foreach or if it's even possible, so I replaced the foreach with:
// I get the 9 counts of $post_blocks correctly.
$block = 0;
while ($block < count($post_blocks))
// If the $block_name doesn't match `blockName` value inside the multi-dimensional array, then continue iterating until the end and then return false.
// If ['attrs']['id'] is not set, return false.
// At last, if we have a blockName and a ID and the selector is set, return ['attrs']['data'][$selector]
}
All help will be appreciated! It makes no sense to me, but if someone can assist, I'd be forever grateful!
It's basically the same as your foreach loop, you just set the iteration variable by indexing the array, and increment the index manually.
$block_num = 0;
while ($block_num < count($post_blocks)) {
$block = $post_blocks[$block_num];
if ($block_name == $block['blockName']) {
if (!isset($block['attrs']['id'])) {
return false;
}
if (isset($block['attrs']['data'][$selector])) {
return $block['attrs']['data'][$selector];
} else {
break;
}
}
$block_num++;
}
I'm not sure why your colleagues think this is preferable.
If there's a company coding style they want you to follow, why don't you ask them what it should be?

Next element in array

I create such function for find next element from my array
protected function getProject($array, $slug, $next = 999)
{
foreach($array as $key => $project)
{
if($project->getSlug() == $slug) {
return $this->getNextProject($array, $slug, $key + 1);
}
}
}
But in result i have error "Maximum function nesting level of '100' reached, aborting". I know that there is a getSlug() isset. Please, help me solve this problem
I think the best way it's create new function, witch look like this:
public function yourAction($array, $key)
{
foreach($array as $index => $project) {
if($index == $key) {
return $project->getProduct();
}
}
}
And then usa like
if($project->getProduct() == $slug) {
$nextProduct = $this->yourAction($array, $key + 1)
return $nextProject;
}
Increase the limit of xdebug in php.ini
For example, making nesting level limit to 300:
xdebug.max_nesting_level=300
You can also comment out this line in php.ini to remove limiting constrain:
";xdebug.max_nesting_level"
You have an infinite loop/infinite recursion going on here.
foreach($array as $key => $project)
{
if($project->getSlug() == $slug) {
return $this->getNextProject($array, $slug, $key + 1);
}
// ...
}
If there are any $array elements that match the condition $project->getSlug() == $slug, it will call getNextProject() again, which will run this foreach again thus calling getNextProject() again... and so on.
You need to reorder/re-think your logic here. You can try to move the if($next != 999) { to the top of the foreach.
Try this:
protected function getNextProject($array, $slug, $next = 999)
{
foreach($array as $key => $project)
{
if($next != 999 && $next == $key) {
return $project->getSlug();
}
if($project->getSlug() == $slug) {
return $this->getNextProject($array, $slug, $key + 1);
}
}
}
Or better yet, you can just do this with one loop and no recursion:
protected function getNextProject($array, $slug)
{
foreach($array as $key => $project)
{
if($project->getSlug() == $slug && isset($array[$key+1])){
return $array[$key+1]->getSlug();
}
}
}
Just find the 1st element that matches the $slug, then get the next one and return its slug.

How to Call a PHP Function that is in an Array using Foreach

I couldn't find anything that answers my question so here it is:
I need to have a foreach loop to take each function inside of an array and run each and check if it returns true, simple enough. Like this:
$array_name = array(function1(),function2(),function3());
foreach($array_name as &$value) {
/* run each function */
/* checks if it returns true */
}
This may be so easy I just don't see it, but I can't find any definitive documentation on how to correctly implement this.
$array_name = array('function1', 'function2', 'function3');
foreach($array_name as $value) {
if($value()) {
// do stuff if the function returned a true-ish value
}
}
Another option to call the function would be call_user_func($value).
Try it:
$array_name = array('function1','function2','function3');
foreach($array_name as &$value) {
if(function_exists($value) && ($value())) {
//function exists and it returns true
}
}
Try to adopt things from : http://php.net/manual/en/functions.variable-functions.php
foreach($functionName as $arg) {
$arg();
}
But as you question contains:
$array_name = array(function1(),function2(),function3());
Make sure "function1()" is used in your array. So we can have:
foreach($functionName as $arg) {
$check = $arg;
if($check != false){
//Do stuff here
}else{
//Do stuff here
}
}

Is there a better way to loop two associative arrays to match their values?

I'm parsing the HTTP_ACCEPT_LANGUAGE header to get users' language and I'm building a class to do that.
Actually I build an associative array ("$this->user_lang") where keys are the language (such as "en-us", "it-it", "it-ch" etc) and the value is the quality factor (so I can order languages).
Then I have another associative array named "$this->installed_langs" where I declare supported language and locales (in the form "en" => "en_US", "it" => "it_IT").
All I want to do is try to match one of the key of "$this->user_lang" with one of the "$this->installed_langs" (without care of the local zone after the "-") and return the first occurrence (with no care for other matching case).
I ended up with this method but it seems a bit too complex...
public function show() {
$g_locale = null;
foreach ($this->user_lang as $lang => $q) {
foreach($this->installed_langs as $valid => $locale) {
if (strpos($lang, $valid) !== false) {
if ($g_locale === null) $g_locale = $locale;
}
}
}
// debug:
echo $g_locale;
}
I hope I have explained it well, btw if you need more informations, please, ask me.
Try this
public function show() {
$g_locale = null;
foreach ($this->user_lang as $lang => $q) {
if ( array_key_exists( $lang, $this->installed_langs ) ) {
$g_locale = $this->installed_langs[$lang];
}
}
}
function show() {
$g_locale = null;
foreach ($this->user_lang as $lang => $q) {
$_key=explode($lang, '-'); // 'en-us' => 'array('en', 'us')
$key=$_key[0]; // 'en'
if ( array_key_exists( $key, $this->installed_langs ) ) {
$g_locale = $this->installed_langs[$key];
}
}
}

PHP: Modifying array with unknown structure at runtime; what is the most elegant solution?

PROBLEM
I have a function that takes in a nested array where the structure and nesting of the array is unknown at run-time. All that is known is some of the target fieldnames and desired values of some of the leafs.
QUESTIONS
1) I am hoping to modify this unknown structure and still have the code be readable and easily understood by fellow programmers. What (if any) solution will allow me to do things like this in PHP?
// Pseudo-code for things I would like to be able to do
// this is kinda like the same thing as XPATH, but for native PHP array
// find *every* fname with value of "Brad" and change it to "Brian"
$mydata->find_all('*:fname')->where_value_eq('Brad')->set_equal_to('Brian');
// find *the first* fave_color and set it to "Green"
$mydata->find('*:fave_color')->get(0)->set_equal_to('Green');
2) If there is nothing out there that will let me do this, is there something, anything, that at least comes close to the spirit of what I am hoping to accomplish here?
SAMPLE ARRAY
$mydata = array(
'people' => array(
array('fname'=>'Alice'),
array('fname'=>'Brad'),
array('fname'=>'Chris'),
),
'animals' => array(
array('fname'=>'Dino'),
array('fname'=>'Lassie'),
array('fname'=>'Brad'),
),
'settings' => array(
'user_prefs'=>array(
'localhost'=>array(
'fave_color'=>'blue',
),
),
),
'places' => array(
array('state'=>'New york',
'cities'=>array(
'name'=>'Albany',
'name'=>'Buffalo',
'name'=>'Corning',
),
'state'=>'California',
'cities'=>array(
'name'=>'Anaheim',
'name'=>'Bakersfield',
'name'=>'Carlsbad',
),
),
),
);
Although I maintain that you should stick with explicit manipulation as in my previous answer, boredom and intrigue got the better of me ;)
It probably has holes (and clearly lacks docs) but if you insist on this route, it should get you started:
class Finder {
protected $data;
public function __construct(&$data) {
if (!is_array($data)) {
throw new InvalidArgumentException;
}
$this->data = &$data;
}
public function all() {
return $this->find();
}
public function find($expression = null) {
if (!isset($expression)) {
return new Results($this->data);
}
$results = array();
$this->_find(explode(':', $expression), $this->data, $results);
return new Results($results);
}
protected function _find($parts, &$data, &$results) {
if (!$parts) {
return;
}
$currentParts = $parts;
$search = array_shift($currentParts);
if ($wildcard = $search == '*') {
$search = array_shift($currentParts);
}
foreach ($data as $key => &$value) {
if ($key === $search) {
if ($currentParts) {
$this->_find($currentParts, $value, $results);
} else {
$results[] = &$value;
}
} else if ($wildcard && is_array($value)) {
$this->_find($parts, $value, $results);
}
}
}
}
class Results {
protected $data;
public function __construct(&$data) {
$this->data = $data;
}
public function get($index, $limit = 1) {
$this->data = array_slice($this->data, $index, $limit);
return $this;
}
public function set_equal_to($value) {
foreach ($this->data as &$datum) {
$datum = $value;
}
}
public function __call($method, $args) {
if (!preg_match('/^where_?(key|value)_?(eq|contains)$/i', $method, $m)) {
throw new BadFunctionCallException;
}
if (!isset($args[0])) {
throw new InvalidArgumentException;
}
$operand = $args[0];
$isKey = strtolower($m[1]) == 'key';
$method = array('Results', '_compare' . (strtolower($m[2]) == 'eq' ? 'EqualTo' : 'Contains'));
$ret = array();
foreach ($this->data as $key => &$datum) {
if (call_user_func($method, $isKey ? $key : $datum, $operand)) {
$ret[] = &$datum;
}
}
$this->data = $ret;
return $this;
}
protected function _compareEqualTo($value, $test) {
return $value == $test;
}
protected function _compareContains($value, $test) {
return strpos($value, $test) !== false;
}
}
$finder = new Finder($mydata);
$finder->find('*:fname')->where_value_eq('Brad')->set_equal_to('Brian');
$finder->find('*:fave_color')->get(0)->set_equal_to('Green');
$finder->find('places:*:cities:*:name')->where_value_contains('ba')->set_equal_to('Stackoton');
print_r($mydata);
There's certainly no native solution for this and the syntax is rather strange. If you want the code to "be readable and easily understood by fellow programmers" please stick to methods that we're used to working with ;)
foreach ($mydata as $type => &$data) {
foreach ($data as &$member) {
if (isset($member['fname']) && $member['fname'] == 'Brad') {
$member['fname'] = 'Brian';
}
}
}
It's admittedly more verbose, but there's much less chance of confusion.

Categories