I'm using Laravel and am attempting to write an artisan command that accepts an argument, $content, that then needs to be transposed to an array. For example:
// An example of a value that is being passed in from the CLI and stored as $content
$content = "['div']['div'][0]['h2']['a']";
To clarify, $content is then being passed in as a function argument where it will be used to pull out the values from the actual result set. In other words, I'm receiving a response from an API and $content is the path to the values that I need to pull out of the response. For example, the hard coded version of the implementation looks like:
$result = $report['div']['div'][0]['h2']['a'];
Now, $result contains the values that I need to process. However, I'm trying to make this dynamic so I don't repeat myself each time I add in a new data source to check for reports. Currently, I have 4 functions each with hard-coded paths to the desired result; the goal is to make it so that I can pass in an argument that contains the path to the desired values and refactor my code so I only have the one function which accepts the path to the values as an argument.
I hope that clarifies the end-goal and why I'm only referencing keys, not a value.
Anyway, back to the problem at hand. I've attempted different variations of the following:
// Attempting to parse the string into an array
$arr = explode("]", trim(str_replace("[","",$content), "]"));
This results in the following array:
array (size=5)
0 => string ''div'' (length=5)
1 => string ''div'' (length=5)
2 => string '0' (length=1)
3 => string ''h2'' (length=4)
4 => string ''a'' (length=3)
But, what I need is for the array to be formatted like the following:
$array = ['div']['div'][0]['h2']['a']
I attempted to do a foreach($array as $element) over $arr and do an array_push for each element, but that resulted in the same output as $arr.
How can I loop over the string and parse it into an array? Additionally, I need 0 to remain as an index, and not be type casted as a string. And, one last note, the value of $content will be completely different each time, so I need it to be quite flexible. The only part that will remain constant is the [ and ] will always encapsulate the keys.
I'd love to hear how others would solve this seemingly trivial problem (I've taken a few years off from programming and apparently have forgotten more than I care to admit ;) ). I honestly thought that the str_replace and explode was going to provide me the result I was expecting...
But, after re-reading the php.net/explode doc, it's always going to cast each element as a string (thus overriding my 0 index), and I have no idea how to turn it into a nested array, instead of a simple, flat array.
I look forward to your advice and insight. Thanks.
EDIT:
Including the function that is making use of the arguments to help provide some greater clarity.
private function yql_check_website($url, $xpath, $content) {
require_once('../vendor/OAuth.php');
$statement = "select * from html where url='{$url}' and xpath='{$xpath}' ";
$cc_key = $this->key;
$cc_secret = $this->secret;
$url = "http://query.yahooapis.com/v1/public/yql";
$args = array();
$args["q"] = $statement;
$args["format"] = "json";
$consumer = new OAuthConsumer($cc_key, $cc_secret);
$request = OAuthRequest::from_consumer_and_token($consumer, NULL,"GET", $url, $args);
$request->sign_request(new OAuthSignatureMethod_HMAC_SHA1(), $consumer, NULL);
$url = sprintf("%s?%s", $url, OAuthUtil::build_http_query($args));
$url .= "&env=store://datatables.org/alltableswithkeys";
$ch = curl_init();
$headers = array($request->to_header());
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
$rsp = curl_exec($ch);
$report = json_decode($rsp, true);
// Dynamically inspect the $report object for the xpath content
$result = $report . $content;
unset($report);
return $result;
}
So, $report contains the entire API response, but the only thing I need is the content that is provided in $report['div']['div'][0]['h2']['a'] (for this example, at least, the path is different for each report that I'm scraping). So, the reason I'm trying to convert the command line argument into an array is so that it can be used in the above code where $content is being called in order to navigate the API's response and return the values from that segment.
I hope that makes more sense. And, if there is a better way to achieve this end-goal, feel free to mention it. I may be taking the wrong approach...
Laravel has a few methods for array access that provide "dot" access to arrays, including array_get. The method prototype looks like this:
function array_get(array $array, string $key, mixed $default = null)
So, in your case, you could write:
$results = array_get($report, "div.div.0.h2.a");
You could just use preg_match_all here..
$content = "['div']['div'][0]['h2']['a']";
$expression = /\['([a-zA-Z0-9]+)\']/;
$results = preg_match_all( $expression, $content, $matches );
print_r( $matches );
If you have to have the array be like what you're saying, you could do something like this (for starters - this is a little ugly though as it is...)
// array( ['div']['div'][0]['h2']['a']
// visualizing...
// array( 'div' => array ( 'div' => 'array( "0" => array( "h2" => array( "a") ) ) ) );
function createDepth( $source, $currentKey, $nextDepth = null ) {
if( !is_array( $nextDepth ) ) {
$source[] = array( $currentKey => $nextDepth );
} else {
$nextKey = array_shift( $nextDepth );
$source[] = createDepth( array( $currentKey => array() ), $nextKey, $nextDepth );
}
return source;
}
// Edit: doh, unset the first key..
unset( $matches[0] );
// Continue from here.
$currentKey = array_shift( $matches );
$holder = array();
$final = createDepth( $holder, $currentKey, $matches);
print_r( $final );
Note: All the above is untested...
Is this what you want ?
$str = "['div']['div'][0]['h2']['a']";
$str = preg_split('/]\[/', substr(str_replace("'", "", $str), 1, -1));
print_r($str);
Array (
[0] => div
[1] => div
[2] => 0
[3] => h2
[4] => a
)
Related
I get and error array to string conversion error on line 11
I need to compare $result array with $file array and then over write FILE with $result data. In other words, FILE and the data it contains is continuously being updated with $result
compare -> overwrite -> repeat at next execution.
Note: .db file is empty at first cycle but becomes populated at first write.
sample code with Array to string conversion error:
<?php
$id = $argv[1]; //variable for inbound
$result = array(
'return' => array(
array(1,2,3),
array(6,2,3),
array(3,2,3),
)
);
function getdiff($new, $old) {
$diff = array_intersect($new, $old);
return $diff;
}
$old = file_exists('1.db') ? json_decode(file_get_contents('1.db'), 1) : array();
$arrayDiffresult = getdiff( $result, $old);
file_put_contents('1.db', json_encode($result));
print_r(
getdiff($result, $old)
);
?>
I have a second method I have tried and I get the same error, at the comparison point line 9.
$result = array(
'return' => array(
array(1,2,3),
array(5,2,3),
array(3,2,3),
)
);
$lines = file("myDB.db");
$arrayDiffresult = array_diff ( $result['return'], $lines);
file_put_contents('myDB.db', print_r($result['return'], true));
I believe array_intersect is only used in one dimensional arrays, and it is attempting to treat the nested arrays as a string for equality comparison. However looking at the documentation show the function array_uintersect where you can write your own comparison function as a callback. You didn't provide much information as to what the requirements are but if you do I'd be happy to help
I am fetching values from online using curl from given url working fine, but here the problem is, giving combined string like - first url had one value returning and next url returning with 5 values so total six values displaying as like this, "testdata1testdata2testdata3testdata4testdata5testdata6testdata7" i need to insert these data to mysql it is storing like string how to separate by comma.
function data($urls,$v)
{
foreach ($urls as $url => $key)
{
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $key);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 20);
curl_setopt($curl, CURLOPT_USERAGENT, $useragent);
$str = curl_exec($curl);
curl_close($curl);
$html= str_get_html($str);
foreach($html->find('.name') as $element)
{
$grab = $element->innertext;
$sql = $this->db->prepare("UPDATE table SET word='$grab' where url='$v[$url]'");
}
}
}
UPDATE
You wrote in comments, that there are no numbers in the variables past. In that case it is impossible to guess where the delimeters should be unless you know i.e.
the size of each parameter (maybe it is constant?)
have got array of ALL possible variables?
know something more about the variables structure?
If You don't, the answer is: You can't, sorry. As Shah Rukh suggested in comments, try to use i.e. json to pass the parameters to another page / subpage / script.
If every variable is ended with a number, you can try this solution:
(I assumed that every variable passed is ended up with a number, so You must be really careful and be sure that this is the exact format, that the data will always be sent in).
function every($matches) {
return $matches[1].',';
}
$input = 'testdata1testdata2testdata3testdata4testdata5testdata6testdata7';
$input = preg_replace_callback( "|(\d+)|", "every", $input);
$input = explode(',', rtrim($input, ","));
print_r($input);
Will print out:
Array
(
[0] => testdata1
[1] => testdata2
[2] => testdata3
[3] => testdata4
[4] => testdata5
[5] => testdata6
[6] => testdata7
)
I hope it helps, however I think that you should change some logic of your application, and send the data between pages in a more user friendly format as json for example. Best regards!
PS. If You want to get rid of "test", you can do for example:
foreach ($input as $key => $value) $input[$key] = substr($value, 4);
But remember to be careful when modifying arrays at the same time you are iterating them through.
I have a multidimensional array, e.g.:
$values = array(
'one' => array(
'title' => 'Title One',
'uri' => 'http://example.com/one',
),
'two' => array(
'title' => 'Title Two',
'uri' => 'http://example.com/two',
),
);
...and I'd like to parse through that with a closure in my implode function, à la:
$final_string = implode(' | ', function($values) {
$return = array();
foreach($values as $value)
$return[] = '' . $value['title'] . '';
return $return;
});
However, this usage yields an Invalid arguments passed error. Is there syntax that I'm missing which will make this use of closures possible? I'm using PHP v5.3.16.
Use array_map:
$final_string = implode(' | ', array_map(function($item) {
return '' . $item['title'] . '';
}, $values));
I trust you'll properly escape the values as HTML in your real code.
As to why this works and your code doesn't, you were passing a function as the second argument to implode. Frankly, that makes little sense: you can join a bunch of strings together, or maybe even a bunch of functions, but you can't join a single function together. It sounds strange, especially if you word it that way.
Instead, we first want to transform all of the items in an array using a function and pass the result of that into implode. This operation is most commonly called map. Luckily, PHP provides this function as, well, array_map. After we've transformed the items in the array, we can join the results.
It seems that you need to assign the function to a variable, and then pass it through to make it work.
$fn = function($values) {
$return = array();
foreach($values as $value)
$return[] = '' . $value['title'] . '';
return $return;
};
$final_string(' | ', $fn($values));
echo $final_string;
I am not sure what the reason is, though, and will need to check it in a little more depth to be able to give you a proper reason.
You can see the code working here
EDIT : Converted this answer to a community wiki so that everyone can contribute here.
EDIT : Explanation by #kmfk
When you pass the closure directly to the implode method - which explicitly wants a second argument of type array, it essentially checks the instanceof - hence the invalid argument. The implode function does not expect mixed type and doesn't know to execute the closure to get an array.
When you first assign that function to a variable, it causes PHP to first evaluate that variable and it ends up passing the returned value from the function into implode.
In that case you're returning an array from the function and passing that into implode - that checks out.
That anonymous function would be instanceof Closure, and
Closure !== array
Ashwin's answer is correct. Here's why:
When you pass the closure directly to the implode method - which explicitly wants a second argument of type array, it essentially checks the instanceof - hence the invalid argument. The implode function does not expect mixed and doesn't know to execute the closure.
When you first assign that function to a variable, it causes PHP to first evaluate that variable and it ends up passing the returned value from the function into implode.
In that case you're returning an array from the function and passing that into implode - that checks out.
edit/adding: that anonymous function would be instanceof Closure.
Closure !== array
You can't use implode to what your trying to achieve, because implode only accept an array as the second argument.
You can try something like this.
$values = array(
'one' => array(
'title' => 'Title One',
'uri' => 'http://example.com/one',
),
'two' => array(
'title' => 'Title Two',
'uri' => 'http://example.com/two',
),
);
$links = array();
foreach ($values as $value) {
$links[] = "<a href='" . $value['uri'] . "'>" . $value['title'] . "</a>";
}
$string = implode(" | ", $links);
function implode_callback( $array, $separator = '', $callback = false )
{
return implode(
$separator,
$callback === false ?
$array : array_map( $callback, $array )
);
}
Example use:
$tab = array( 1, 2, 3 );
echo implode_callback( $tab, '<hr>', function($x) { return "<p>$x</p>"; } );
See example code
I don't know how to make this working. I want to make arrays from URL:
index.php?address=someaddress&telephone=12345&customer=Customer Name&p_name[0]=ProductOne&p_name[1]=ProductTwo&p_price[0]=1&p_price[1]=10&p_name[2]...
There is an api, which is working like this:
$api‐>addItem(array(
'name' => 'ProductOne',
'price' => '123',
));
$api‐>addItem(array(
'name' => 'ProductTwo',
'price' => '32',
));
Is there any way to make arrays like this (=api request $api->addItem(array) from the URL? $api‐>addItem(array can be used multiple times.
EDIT: Thanks dqhendricks and Rocket for pointing out that you can use parse_str() to do the same thing.
$q = parse_str(parse_url($url, PHP_URL_QUERY));
Or you could use this (the long way):
function parse_query($var) {
$var = parse_url($var, PHP_URL_QUERY);
$var = html_entity_decode($var);
$var = explode('&', $var);
$arr = array();
foreach($var as $val) {
$x = explode('=', $val);
$arr[$x[0]] = $x[1];
}
unset($val, $x, $var);
return $arr;
}
Use like this:
$url = "http://someurl.com/click?z=26&a=761";
print_r(parse_query($url));
Do you have control over the URL? If so, I'd change how you send your values.
Instead of:
name1=ProductOne&price1=123&name2=ProductTwo&price2=32
I'd use:
name[]=ProductOne&price[]=123&name[]=ProductTwo&price[]=32
The [] turn them into arrays, meaning $_GET['name'] is now an array. then you can foreach over it.
foreach($_GET['name'] as $k=>$v){
$api->addItem(array(
'name' => $v,
'price' => $_GET['price'][$k]
));
}
// extract the query from the url string
$url = parse_url('sample.php?name1=ProductOne&price1=123&name2=ProductTwo&price2=32', PHP_URL_QUERY);
// process into array so that first element is a key, and second element is a value
parse_str($url, $output_array);
// now $output_array contains the query's variables.
The bigger question is why would you want to do this when these variables are already contained in $_GET?
sample.php?name[]=Product1&name[]=Product2
Then in your PHP, you can see:
print_r($_GET['name']);
I'm not sure if I understood you right, but if what you're looking for is converting a querystring to an array you can user the pares_str function. in order to get only the querystring from a url you can use the parse_url function
$url = "sample.php?some_var=someval&s=4&bla=bla";
$url_parts = parse_url($url);
$qs = $url_parts["query"];
$params = array();
parse_str($qs,$params);
var_dump($params);
Like dqhendricks suggested, would it not pay to just use the raw gets and then append them to an array. I am assuming that you will always know what will be in the URL
$array_of_gets = array("address" => $_GET['address'],"telephone" => $_GET['telephone']);
and so on...
The title of this question is self-explanatory.
I've heard I can mimic this using http_build_query, but I'd rather use a function that's meant for this.
Input example:
$assoc = array(
"fruit" => "banana",
"berry" => "blurberry",
"vegetable" => "lettice"
);
Desired output (I get this with http_build_query):
string(46) "fruit=banana,berry=blurberry,vegetable=lettice"
output from reversal wanted is the same as input - that's my current problem.
Implode with
serialize($array);
Explode with
unserialize($array);
Found a function in the php .net comments for implode:
function implode_with_key($glue = null, $pieces, $hifen = ',') {
$return = null;
foreach ($pieces as $tk => $tv) $return .= $glue.$tk.$hifen.$tv;
return substr($return,1);
}