eval() with file_get_contents() .. different results - php

I am trying to use eval() with json_decode(file_get_contents()) and am getting what appears to be different results when eval'ing and hard-coding the values. Am I just missing something easy?
I am using PHP Version 5.3.24, and do not have the ability to change at the moment. If it is critical to eval() support, I can start the process of getting it changed. I have not found anything that suggests I do not have eval() support in my current PHP implementation.
When I run Example1('key') I get NULL back from the eval() function. When I run Example2('key') I get back an array based on json data that looks like this:
{ key_list: [ { data1_list: [ { subkey1: "data", subkey2: 0 }, .. ], .. ] }
Here is Example1():
function Example1($key) {
$endPoint = 'http:'.'//some.website.com/json/'.$key;
$evalCommand = sprintf('json_decode(file_get_contents("%s"))->%s_list[0];', $endPoint, $key);
echo '$evalCommand = |'.$evalCommand.'|<br />';
$resultsArray = eval($evalCommand);
return $resultsArray;
}
Here is Example2():
function Example2($key) {
$endPoint = 'http:'.'//some.website.com/json/'.$key;
$resultsArray = json_decode(file_get_contents($endPoint))->key_list[0];
return $resultsArray;
}

Rather than using eval, it may be able to use more standard code and using dynamic object access.
The name of the URL is OK, but for the field there is a bit of juggling. This sets the name in $field, but I couldn't get it to do that part and the array part in one line. Here it uses ->$field to get the data and the [0] is on the return...
function Example2($key) {
$endPoint = 'http://some.website.com/json/'.$key;
$field = $key.'_list';
$resultsArray = json_decode(file_get_contents($endPoint))->$field;
return $resultsArray[0];
}

Related

How to write an unescaped javascript filter in php-latte v3

I'm basically trying to write a json_encode filter, by which I'm hoping to get a raw json object, but what I'm getting instead, is an escaped string of the json object.
Expected result:
{"foo":"bar"}
Actual result:
"{\"foo\":\"bar\"}"
Right now the only way I can get it the way I want, is by using the noescape filter, but this makes it unnecessarily more ugly
{$object|json_encode|noescape}
My json_encode filter
public static function json_encode(FilterInfo $info, mixed $value): string {
$info->contentType = ContentType::JavaScript;
return json_encode($value);
}
You can try something like {do printf('%s',json_encode($object)) } printf isn't restricted unlike echo and print within the context of the php and do tags. Even if it was restricted, you can define your own function and have it echo from there.
But it looks like noescape is a very special filter which doesn't actually appear in the core filter list, it is the compiler/nodes and essential/nodes sections which check in multiple places for the presence of the noescape filter within the filter list then make decisions based on it.
➜ latte grep -r 'noescape' .
./latte/src/Latte/Essential/Nodes/BlockNode.php: if ($node->modifier->hasFilter('noescape') && count($node->modifier->filters) === 1) {
./latte/src/Latte/Essential/Nodes/BlockNode.php: throw new CompileException('Filter |noescape is not expected here.', $tag->position);
./latte/src/Latte/Essential/Nodes/IncludeFileNode.php: $noEscape = $this->modifier->hasFilter('noescape');
./latte/src/Latte/Essential/Nodes/IncludeBlockNode.php: $noEscape = $this->modifier->hasFilter('noescape');
./latte/src/Latte/Compiler/Nodes/Php/FilterNode.php: NoEscape = 'noescape';
./latte/src/Latte/Compiler/Nodes/Php/ModifierNode.php: } elseif ($name === 'noescape') {
./latte/src/Latte/Compiler/Nodes/Php/ModifierNode.php: if ($name === 'noescape') {
./latte/src/Latte/Compiler/Nodes/Php/ModifierNode.php: $noescape = true;
./latte/src/Latte/Compiler/Nodes/Php/ModifierNode.php: if ($this->escape && empty($noescape)) {
➜ latte
I'm afraid trying to find a simple way to do this without having to use noescape would push latte to its limits.
It might be possible to dynamically slap on the noescape filter if your extension finds the json_encode filter being used. Otherwise it looks like the way to do this is like so:
{$object|escapeJs|noescape}
You could also try a different syntax like:
{do json_encode_output($object) }
and define a global function like so:
function json_encode_output($object)
{
echo json_encode($object);
}
It might also be possible to define your own tag like:
{json $output}
But considering how latte is designed, I'd probably just settle with the more verbose:
{$object|escapeJs|noescape}
Or I might actually reach for:
{json_encode($object)|noescape}
Edit:
I think I actually just figured it out. Simply echo from your function.
public static function json_encode(FilterInfo $info, mixed $value): string {
echo json_encode($value);
return '';
}
Found the answer in their forum.
This isn't very obvious from the documentation, but seems like since latte understands the context automatically, you don't really need any filter for json_encode, just need to write it as: var jsonObject = {$object} and it will automatically encode it to json

Easiest way to check if multiple POST parameters are set?

Hi so I want to know the easiest way to check if multiple POST parameters are set. Instead of doing a long if check with multiple "isset($_POST['example'])" linked together by "&&", I wanted to know if there was a cleaner way of doing it.
What I ended up doing was making an array and looping over it:
$params_needed = ["song_name", "artist_name", "song_release_date",
"song_genre", "song_medium"];
I would then call the function below, passing in $params_needed to check if the parameter names above are set:
function all_params_valid($params_needed) {
foreach ($params_needed as $param) {
if (!isset($_POST[$param])) {
error("Missing the " . $param . " variable in POST request.");
return false;
}
}
return true;
}
if (all_params_valid($params_needed)) {
$song_name = $_POST["song_name"];
$artist_name = $_POST["artist_name"];
$song_release_date = $_POST["song_release_date"];
$song_genre = $_POST["song_genre"];
$song_medium = $_POST["song_medium"];
...
}
However when I do this, it gets stuck on the first index and says "Missing the song_name variable..." despite actually including it in the POST request, and I'm not sure why this is happening. The expected behavior would be for it to move on and tell me the next parameter "artist_name" is not set, but this doesn't happen.
I personally like using array_diff for this issue.
PHP array_diff documentation
What you care about is your expected input is the same as the given input.
So you can use array_diff like this:
$params_needed = ["song_name", "artist_name", "song_release_date",
"song_genre", "song_medium"];
$given_params = array_keys($_POST);
$missing_params = array_diff($params_needed, $given_params);
if(!empty($missing_params)) {
// uh oh, someone didn't complete the form completely...
}
How I approach this is by using array_map() so I can return all the values in the array whilst checking if it isset()
PHP 5.6 >
$args = array_map(function($key) {
return isset($_POST[$key]) ? array($key => $_POST[$key]) : someErrorMethod($key);
}, ["song_name", "artist_name", "song_release_date", "song_genre", "song_medium"]);
PHP 7+
$args = array_map(function($key) {
return array($key => $_POST[$key] ?? someErrorMethod($key));
}, ["song_name", "artist_name", "song_release_date", "song_genre", "song_medium"]);
Your error method could look something like this:
function someErrorMethod($key) { die("$key cannot be empty."); }
Inside of your $args variable, you will have an array of key => value. For example,
echo $args['song_name'];

php use value as a variable name (for a key) from an array to unpack a resultset from Drupal

I will be reusing a Drupal db_query result set unpacking function many, many times in my code for a variety of different queries - I am using O-O and as such I want to reuse it and be as 'DRY' as possible.
Therefore I have tried to strip it down to the most generic functions so that as long as the $columns supplied match the columns used in the query and similarly in the $resultset, I can loop and assign values to keys, as is shown, and return a $rows[].
I've not yet come across the issue of trying to use a variable's value as a variable name (the $key), if it's just something I should avoid entirely, please say.
foreach($this->resultSet as $aRecord) {
$c = 0;
while (isset($this->columns[$c])) {
$value = $this->columns[$c];
$rows[$i] = array(
$key[$this->columns[$c]] => $aRecord->$value,
);
$c++;
}
$i++;
}
I've read through the following and am beginning to think this is just knowledge I'm missing in my PHP experience so far.
Can I use a generated variable name in PHP?
PHP use function return value as array
https://wiki.php.net/rfc/functionarraydereferencing
It felt wrong, and someone once told me that if you have to start writing complex functions in PHP you've probably missed an available function PHP already offers.. so true... thanks to (at the time of writing...) 'MrCode' for this suggestion.
$this->sql = "SELECT foo, bar FROM foobar";
$this->result = db_query($this->sql);
if ($this->result->rowCount() > 0) {
while ($row = $this->result->fetchAssoc()) {
$this->resultArray[] = $row;
}
}

How to Pass Class Variables in a Function Parameter

The main function of the example class uses the reusableFunction twice with different data and attempts to send that data to a different instance variable ($this->result1container and $this->result2container) in each case, but the data doesn't get into the instance variables.
I could get it to work by making reusableFunction into two different functions, one with array_push($this->result1container, $resultdata) and the other with array_push($this->result2container, $resultdata), but I am trying to find a solution that doesn't require me to duplicate the code.
My solution was to try to pass the name of the result container into the function, but no go. Does somebody know a way I could get this to work?
Example Code:
Class Example {
private $result1container = array();
private $result2container = array();
function __construct() {
;
}
function main($data1, $data2) {
$this->reusableFunction($data1, $this->result1container);
$this->reusableFunction($data2, $this->result2container);
}
function reusableFunction($data, $resultcontainer) {
$resultdata = $data + 17;
// PROBLEM HERE - $resultcontainer is apparently not equal to
// $this->result1container or $this->result2container when I
// try to pass them in through the parameter.
array_push($resultcontainer, $resultdata);
}
function getResults() {
return array(
"Container 1" => $this->result1container,
"Container 2" => $this->result2container);
}
}
(If this is a duplicate of a question, I apologize and will happily learn the answer from that question if somebody would be kind enough to point me there. My research didn't turn up any answers, but this might just be because I didn't know the right question to be searching for)
It looks to me like you want to be passing by reference:
function reusableFunction($data, &$resultcontainer) {
...
If you don't pass by reference with the & then you are just making a local copy of the variable inside reuseableFunction .
You are changing the copy, not the original. Alias the original Array by referenceDocs:
function reusableFunction($data, &$resultcontainer) {
# ^
And that should do the job. Alternatively, return the changed Array and assign it to the object member it belongs to (as for re-useability and to keep things apart if the real functionality is doing merely the push only).
Additionally
array_push($resultcontainer, $resultdata);
can be written as
$resultcontainer[] = $resultdata;
But that's just really FYI.
You may pass the attributes name as a String to the method like this:
function reusableFunction($data, $resultcontainer) {
$resultdata = $data + 17;
array_push($this->{$resultcontainer}, $resultdata);
}
//..somewhere else..
$this->reusableFunction($data, 'result2Container')
Some php experts wrote some texts about "why you shouldn't use byReference in php".
Another solution would be to define the containers as an array. Then you can pass an "key" to the method that is used to store the result in the array. Like this:
private $results = array();
function reusableFunction($data, $resIdx) {
$resultdata = $data + 17;
array_push($this->$results[$resIdx], $resultdata);
}
//..somewhere else..
$this->reusableFunction($data, 'result2Container');
//..or pass a number as index..
$this->reusableFunction($data, 1);

PHP: Variable in a function name

I want to trigger a function based on a variable.
function sound_dog() { return 'woof'; }
function sound_cow() { return 'moo'; }
$animal = 'cow';
print sound_{$animal}(); *
The * line is the line that's not correct.
I've done this before, but I can't find it. I'm aware of the potential security problems, etc.
Anyone? Many thanks.
You can do that, but not without interpolating the string first:
$animfunc = 'sound_' . $animal;
print $animfunc();
Or, skip the temporary variable with call_user_func():
call_user_func('sound_' . $animal);
You can do it like this:
$animal = 'cow';
$sounder = "sound_$animal";
print ${sounder}();
However, a much better way would be to use an array:
$sounds = array('dog' => sound_dog, 'cow' => sound_cow);
$animal = 'cow';
print $sounds[$animal]();
One of the advantages of the array method is that when you come back to your code six months later and wonder "gee, where is this sound_cow function used?" you can answer that question with a simple text search instead of having to follow all the logic that creates variable function names on the fly.
http://php.net/manual/en/functions.variable-functions.php
To do your example, you'd do
$animal_function = "sound_$animal";
$animal_function();
You can use curly brackets to build your function name. Not sure of backwards compatibility, but at least PHP 7+ can do it.
Here is my code when using Carbon to add or subtract time based on user chosen type (of 'add' or 'sub'):
$type = $this->date->calculation_type; // 'add' or 'sub'
$result = $this->contactFields[$this->date->{'base_date_field'}]
->{$type.'Years'}( $this->date->{'calculation_years'} )
->{$type.'Months'}( $this->date->{'calculation_months'} )
->{$type.'Weeks'}( $this->date->{'calculation_weeks'} )
->{$type.'Days'}( $this->date->{'calculation_days'} );
The important part here is the {$type.'someString'} sections. This will generate the function name before executing it. So in the first case if the user has chosen 'add', {$type.'Years'} becomes addYears.
For PHP >= 7 you can use this way:
function sound_dog() { return 'woof'; }
function sound_cow() { return 'moo'; }
$animal = 'cow';
print ("sound_$animal")();
You should ask yourself why you need to be doing this, perhaps you need to refactor your code to something like the following:
function animal_sound($type){
$animals=array();
$animals['dog'] = "woof";
$animals['cow'] = "moo";
return $animals[$type];
}
$animal = "cow";
print animal_sound($animal);
You can use $this-> and self:: for class-functions. Example provided below with a function input-parameter.
$var = 'some_class_function';
call_user_func(array($this, $var), $inputValue);
// equivalent to: $this->some_class_function($inputValue);
And yet another solution to what I like to call the dog-cow problem. This will spare a lot of superfluous function names and definitions and is perfect PHP syntax and probably future proof:
$animal = 'cow';
$sounds = [
'dog' => function() { return 'woof'; },
'cow' => function() { return 'moo'; }
];
print ($sounds[$animal])();
and looks a little bit less like trickery as the "string to function names" versions.
JavaScript devs might prefer this one for obvious reasons.
(tested on Windows, PHP 7.4.0 Apache 2.4)

Categories