Write php array code to file without evaluating expressions - php

I'm trying to write some code that will generate a php file with an array, like this.
However, in order to create a correct configuration file I need to be able to leave expressions 'as is' within the array. This is because the file will be used by many users and the expressions evaluate based on environmental variables, etc. the user has set up.
So, say we have this key/value in the array I eventually want to output to the file:
[
...
'connection' => $isFork ? $sourceArray['connection'] : config('database.default'),
...
]
When this array is eventually written out to a php file (right now using var_export and file_put_contents) I will see
'connection' => config('database.default')
become
'connection' => 'default_connection',
because the expression is evaluated. What I need is a way to prevent expressions as values in the array from being evaluated but also ensuring
'connection' => $isFork ? $sourceArray['connection']
does evaluate to
'connection' => 'my_connection'
Is there any way to do this?
EDIT: I basically want to do this but in reverse and with expressions.

If I understand you correctly, your solution is to have a string representation of your array so the statements are not evaluated. I would serialize that array and put that string into the file. Tell your peeps to unserialize it right after they receive it. Better yet, json_encode your array which is going to give you a json string. You can put that in via put_file_contents and tell your peeps to json_decode the contents. They can use it as such json_decode($content, TRUE) which will give them back the associative array.
Update
So you want to write straight up PHP. I see that you have connection stuff in your array so I am thinking it is safe to think it is some sort of a configuration file that includes connection settings etc.
// filename should have the .ini at the end
function writeConfig( $filename, $yourArray ) {
$fh = fopen($filename, "w");
// making sure its available
if (!is_resource($fh)) {
return false;
}
// start dumping you array to the file
foreach ($yourArray as $key => $value) {
fwrite($fh, sprintf("%s = %s\n", $key, $value));
}
fclose($fh); // close file
return true;
}
when you want to read it
function readConfigFile( $fileThatMadeAbove ) {
return parse_ini_file($fileThatYouMadeAbove, false, INI_SCANNER_NORMAL);
}
Since it is config info, it may be better to use the ini in php.
If you want to try plain simple solution
$fp=fopen('filename.php','w');
fwrite($fp, "$yourArray");
fclose($fp);
I honestly do not know if you can do "$yourArray" or not and I do not have a place to test it. You most likely need to do a print_r($yourArray) because it is a string that you write to a file which is why I made my recommendation above.
I am out of ideas. Good luck (:

This isn't possible using var_export. The best way I can see to do this is to create a string of the output and use file_put_contents to output this to a file.
This could be achieved by replicating the array structure, e.g.
$arr_str = "[\n";
. "\t'simple_annotations' => false,\n"
. "];";
Or by creating a helper function to use instead of var_export. Something like this:
function var_str($var, $level = 0){
if(is_array($var)) return arr_str($var, $level+1);
elseif(is_string($var)) return '\''.$var.'\'';
elseif(is_numeric($var)) return $var;
elseif(is_null($var)) return 'null';
elseif(is_bool($var)) return ($var ? 'true' : 'false');
}
function arr_str($arr, $level){
$str = "[\n";
foreach($arr as $k => $e){
$str .= str_repeat("\t", $level);
$str .= "'".$k."' => ".var_str($e, $level).",\n";
}
return $str.str_repeat("\t", $level-1).']';
}
print var_str($my_array);

After searching for a few hours I came to the conclusion the only way to take full control over what I wanted to do was to use a templating engine.
The project this is for uses Laravel so I used Blade but any engine would work (I initially tried this with Twig).
I wrote out each section of my config as if it was a regular php array and then used Blade bracketing to encompass the logic needed to find the right value for each key. If the value was not an expression I evaluated the code, and if it was I wrote the expression into a string.
I ended up with this:
//example.blade.php
[
'meta' => '{{{ $isFork ? $data['metadata']['driver'] : 'annotations' }}}',
'connection' => {{{ $isFork ? '\''.$data['connection'].'\'' : 'config("database.default")' }}},
'paths' => {{ var_export(ArrayUtil::get($data['metadata']['paths'], $data['metadata']), true) }},
'repository' => '{{{ ArrayUtil::get($data['repository'], EntityRepository::class) }}}',
'proxies' => [
'namespace' => {{{ isset($data['proxy']['namespace']) ? '\'' . $data['proxy']['namespace'] .'\'' : 'false' }}},
'path' => '{{{ ArrayUtil::get($data['proxy']['directory'], storage_path('proxies')) }}}',
'auto_generate' => {{{ ArrayUtil::get($data['proxy']['auto_generate'], env('DOCTRINE_PROXY_AUTOGENERATE', 'false')) }}}
],
'events' => [
'listeners' => [],
'subscribers' => []
],
'filters' => []
]
and the output:
[
'meta' => 'yaml',
'connection' => config('database.default'),
'paths' => array(
0 => '/proj/app/Models/mappings',
),
'repository' => 'Doctrine\ORM\EntityRepository',
'proxies' => [
'namespace' => false,
'path' => '/proj/storage/proxies',
'auto_generate' => false
],
'events' => [
'listeners' => [],
'subscribers' => []
],
'filters' => []
]
You can see that where I potentially needed to expand an array I used var_export. Other than it was pretty straight forward.

Related

Dynamic variables in PHP callback function

I have a private function to return an array of options, those options indicate a callback and other options such as template, form, etc. Here the code:
/**
* #return array
*/
private function options()
{
$options = [
'general' => [
'form' => GeneralConfigType::class,
'template' => 'general.html.twig',
'title' => 'Configuración General',
'ignoreFields' => ['slider', 'social'],
'uploadedFields' => [],
'callbacks' => ['generalData']
],
'business' => [
'form' => ConfigurationType::class,
'template' => 'business.html.twig',
'title' => 'Configuración de Empresa',
'ignoreFields' => [],
'uploadedFields' => ['image','favicon','login_icon','sidebar_icon'],
'callbacks' => ['businessImage']
],
];
return $options;
}
Now here is my doubt, in addition to indicate the function you have to execute in the key callback, Can I pass on the variables I'm going to need in that callback? I've tried several ways and they haven't worked.
Example:
Before:
'callbacks' => ['generalData']
After:
In this example I'm assigning the '$', but I could do it if the only string, I'm just looking for a way to pass to the callback the variables it needs and no more.
'callbacks' => ['generalData' => '$configurationData, $configuration, $form, $request']
And this code would be where everything would be executed in other method:
if (!empty($options[ 'callbacks' ])) {
foreach ($options[ 'callbacks' ] as $callback => $variables) {
$this->$callback($variables);
}
}
If I understand you correctly, you want to store the name of the variable in the array of options and then use that variable in the callback function.
When I've done this type of thing, I find it easier to just store the variable name as text and leave out the $ from the name stored in the array. I then use a variable variable when retrieving it.
Either way, I think you need a little more code on the execution side. One more loop:
if (!empty($options[ 'callbacks' ])) {
foreach ($options[ 'callbacks' ] as $callback => $variables) {
foreach($variables as $variable){ // extra loop to get the variables
$this->$callback[$$variable];
// This is where it gets tricky, and depends on how you wish to format.
// The variables are currently part of an array, thus the array notation
// above. By using the stored name only, and a variable variable, you
// should be able to get to the var you need
}
}
}
#jcarlosweb, what you need to do is very simple. The short answer is that it can be done using the [call_user_func_array()][1] method.
In the context of your example, the callbacks could be rearranges in the following way ...
'callbacks' => ['generalData' => [$configurationData, $configuration, $form, $request]
Basically, the array keys will be the name of the function to call, and the corresponding array values will be a array of the values of each parameter that is accepted but the callback function. Doing it this way is important because you need to capture the value of the parameters while they are in scope. And this will avoid using eval().
Using the callbacks can be as simple as ...
$options = options();
foreach ($options['callbacks'] as $callback => $params) {
$result = call_user_func_array($callback, $params);
// Do something with $result if necessary
}
I finally got it with the function compact http://php.net/manual/en/function.compact.php
Here's the code:
First I select the variables I need in my options:
'callbacks' => ['businessImage' => ['configurationData', 'configuration', 'form', 'request']]
Second I call the variables with compact, but I had to use extract here because if I didn't configurationData variable wasn't modified, which I don't understand since I had previously referenced it.
if (!empty($options[ 'callbacks' ])) {
foreach ($options[ 'callbacks' ] as $callback => $variables) {
$variables = compact($variables);
$this->$callback($variables);
extract($variables);
}
}
Third callback applied and referenced:
/**
* #param array $params
* #return array $configurationData
*/
private function businessImage(&$params)
{
extract($params,EXTR_REFS);
// more code here ......
$configurationData[ "image" ] = $originalImageName;
$configurationData[ "favicon" ] = $originalFaviconName;
$configurationData[ "login_icon" ] = $originalLoginIconName;
$configurationData[ "sidebar_icon" ] = $originalSidebarIconName;
return $configurationData;
}
This works correctly in my website, but as I said before I do not understand why I have to call back the function extract, if I have already passed it referenced in the same callback as you see in my last code.

Laravel 5 passing page info with array

I'm trying to pass data about the page state (navbar links having active class when you are in that exact page), page title. I do so with an indexed array $pageInfo, however I am getting a syntax error and doen't know where?
Also do you think this is a good method or should I use view->share() instead?
public function clases()
{
$pageInfo[] =
(
'page_title' => 'Clases',
'menu_active' => 'CLases',
'sub_menu_active' => '',
);
return view('clases.list', compact('pageInfo'));
}
public function domicilio()
{
$pageInfo[] =
(
'page_title' => 'Clases a domicilio',
'menu_active' => 'Clases',
'sub_menu_active' => 'Clases a domicilio',
);
return view('clases.domicilio', compact('pageInfo'));
I suggest you read PHP basic syntax.
Basically you want to do this:
$pageInfo =
[
'page_title' => 'Clases',
'menu_active' => 'CLases',
'sub_menu_active' => '',
];
Arrays have a syntax of [key => val, ...] in PHP, you're using () as it seems.
Also $someArray[] = someValue, will append the someValue to an existing array, in your case that would create another, unwanted level of your array.
And last, you're not ending the domicilio() function. But I'll assume you just didn't paste it in (you should add } at the end, if that's not the case).

How to hide .env passwords in Laravel whoops output?

How can I hide my passwords and other sensitive environment variables on-screen in Laravel's whoops output?
Sometimes other people are looking at my development work. I don't want them to see these secrets if an exception is thrown, but I also don't want to have to keep toggling debug on and off, or spin up a dedicated site just for a quick preview.
As of Laravel 5.5.13, you can censor variables by listing them under the key debug_blacklist in config/app.php. When an exception is thrown, whoops will mask these values with asterisks * for each character.
For example, given this config/app.php
return [
// ...
'debug_blacklist' => [
'_ENV' => [
'APP_KEY',
'DB_PASSWORD',
'REDIS_PASSWORD',
'MAIL_PASSWORD',
'PUSHER_APP_KEY',
'PUSHER_APP_SECRET',
],
'_SERVER' => [
'APP_KEY',
'DB_PASSWORD',
'REDIS_PASSWORD',
'MAIL_PASSWORD',
'PUSHER_APP_KEY',
'PUSHER_APP_SECRET',
],
'_POST' => [
'password',
],
],
];
Results in this output:
First of all, love the solution by Jeff above.
2nd, if like me you wanna hide all the env variables while still use whoops, here is a solution:
'debug_blacklist' => [
'_COOKIE' => array_keys($_COOKIE),
'_SERVER' => array_keys($_SERVER),
'_ENV' => array_keys($_ENV),
],
Output:
EDIT:
Legend has it that since laravel 7x you would need debug_hide key instead
Thanks Jeff and Raheel for helping out, but I just found a little gotcha:
Even if I clear out all environment keys from _ENV, the same keys are STILL exposed through the _SERVER variables listed.
Adding the code below in config/app.php would hide all environment variables from the whoops page:
'debug_blacklist' => [
'_SERVER' => array_keys($_ENV),
'_ENV' => array_keys($_ENV),
],
I've made a package to solve this problem.
Just install it using
composer require glaivepro/hidevara
Most of the server and all the env variables will be removed. Any password-like fields in $_POST will have their values hidden.
You can also customize it in either blacklist or whitelist approach to show/obfuscate/remove fields however you like.
The solution by #jeff + #raheel is great!!! On a project recently we found we sometimes wanted to whitelist a property or two, so building on the above, you can whitelist specific properties you want to debug with something like:
'debug_blacklist' => [
'_COOKIE' => array_diff(array_keys($_COOKIE), array()),
'_SERVER' => array_diff(array_keys($_SERVER), array('APP_URL', 'QUERY_STRING')),
'_ENV' => array_diff(array_keys($_ENV), array()),
],
If you want to allow that list to be configured via .env, you can do something like:
'debug_blacklist' => [
'_COOKIE' => array_diff(
array_keys($_COOKIE),
explode(",", env('DEBUG_COOKIE_WHITELIST', ""))
),
'_SERVER' => array_diff(
array_keys($_SERVER),
explode(",", env('DEBUG_SERVER_WHITELIST', ""))
),
'_ENV' => array_diff(
array_keys($_ENV),
explode(",", env('DEBUG_ENV_WHITELIST', ""))
),
],
Then in your .env, do something like:
DEBUG_SERVER_WHITELIST="APP_URL,QUERY_STRING"
Cheers!
Usually for local development, we should set the APP_DEBUG environment variable to true. So that we can have better insights of the debugging error and warnings.
But in the production environment, this value should always be false. If the value is set to true in production, you risk exposing sensitive env passwords to your application’s end users.
As of Laravel 5.5.x also provides a solution for it.
You just need to add the debug_blacklist option in your config/app.php configuration file. After adding this option, Laravel will blacklist all the keys mentioned in debug_blacklist option with asterisk.
You can use it with two ways:
Method 1 – Blacklist selective ENV keys and passwords
return [
// ...
'debug_blacklist' => [
'_ENV' => [
'APP_KEY',
'DB_PASSWORD',
],
'_SERVER' => [
'APP_KEY',
'DB_PASSWORD',
],
'_POST' => [
'password',
],
],
];
Method 2 – Blacklist all the ENV keys and passwords
return [
// ...
'debug_blacklist' => [
'_COOKIE' => array_keys($_COOKIE),
'_SERVER' => array_keys($_SERVER),
'_ENV' => array_keys($_ENV),
],
]
Reference Taken From : https://techjeni.com/how-to-secure-and-hide-env-passwords-from-laravel-debug-output/
Laravel 5.6 not works for my.
but this works:
$envKeys = [];
$serverKeys = [];
$cookieKeys = [];
foreach ( $_ENV as $key => $value ) { if(is_string($value)) $envKeys[] = $key; }
foreach ( $_SERVER as $key => $value ) { if(is_string($value)) $serverKeys[] = $key; }
foreach ( $_COOKIE as $key => $value ) { if(is_string($value)) $cookieKeys[] = $key; }
return [
// ...
'debug_blacklist' => [
'_COOKIE' => $cookieKeys,
'_SERVER' => $serverKeys,
'_ENV' => $envKeys,
],
];
I would be grateful for a better solution.
Just Change
APP_DEBUG=true
To:
APP_DEBUG=false
In the .env file.
For Laravel 5.6-5.8:
'debug_blacklist' => [
'_COOKIE' => array_keys(array_filter($_COOKIE, function($value) {return is_string($value);})),
'_SERVER' => array_keys(array_filter($_SERVER, function($value) {return is_string($value);})),
'_ENV' => array_keys(array_filter($_ENV, function($value) {return is_string($value);})),
],
I am also facing this issue in production environment, Laravel 5.7
https://laravel.com/docs/5.7/configuration
Here 3 ways we can reslove this issue.
config/app.php file add below line of code
TIPS #1: Block List for all variable
'debug_blacklist' => [
'_COOKIE' => array_keys($_COOKIE),
'_SERVER' => array_keys($_SERVER),
'_ENV' => array_keys($_ENV),
],
TIPS #2: Block List for specific varaibles (Best Practice)
return [
// ...
'_ENV' => [
'APP_KEY',
'DB_PASSWORD',
'REDIS_PASSWORD',
'MAIL_PASSWORD',
'PUSHER_APP_KEY',
'PUSHER_APP_SECRET',
'AWS_APP_SECRET',
'S3_BUCKET_SECRET',
'SOCKET_APP_SECRET',
'TWILIO_APP_SECRET',
],
'_SERVER' => [
'APP_KEY',
'DB_PASSWORD',
],
'_POST' => [
'password',
],
]
TIPS #3: Debug variable
APP_DEBUG=true to APP_DEBUG=false
NOTE:
Production enviroment keep always Debug False
There's a lot of great answers here (credits to #Jeff and #Raheel and #Benjamin and everyone else), but I would like to have a bit more flexible and universal solution. I extended this snippet intended for the config/app.php file even further:
$debug_blacklist=array();
if(env("DEBUG_VAR_LISTING")!==null)
foreach(explode(",", env("DEBUG_VAR_LISTING", "")) as $i){
global ${"_{$i}"};
if(env("DEBUG_VAR_BLACKLIST_{$i}")!==null)
$debug_blacklist["_{$i}"]=explode(",", env("DEBUG_VAR_BLACKLIST_{$i}", ""));
elseif(env("DEBUG_VAR_WHITELIST_{$i}")!==null)
$debug_blacklist["_{$i}"]=array_diff(
array_keys(${"_{$i}"}),
explode(",", env("DEBUG_VAR_WHITELIST_{$i}", ""))
);
}
return [
'debug_blacklist' => $debug_blacklist,
];
Then you can blacklist and whitelist directly in .env and only if and what you need.
So if you don't really need anything from $_ENV you can block all variables and for example just passwords in $_POST, but show APP_URL and QUERY_STRING from $_SERVER:
DEBUG_VAR_LISTING="SERVER,ENV,POST,COOKIE"
DEBUG_VAR_WHITELIST_SERVER="APP_URL,QUERY_STRING"
DEBUG_VAR_WHITELIST_ENV=""
DEBUG_VAR_BLACKLIST_POST="password"
I struggled with this too for a bit on a dev machine. my solution was to edit vendor/filp/whoops/src/Whoops/Handler/PrettyPageHandler.php and add in:
public function sanitizePrivate($data, $badwords){
foreach ($data as $key=>$value) {
foreach ($badwords as $keyword) {
// dd($key);
if (strpos(strtolower($key), $keyword) !== FALSE) {
$data[$key] = "***************";
}
}
}
return $data;
}
This converts all the incoming data to lowercase and then searches for partial matches so you don't have to specify every variation of password variable names. Then in the handle() function, define terms you want to exclude.
$badwords = array("password", "pwd", "secret", "key", "token", "salt", "mail");
$_SERVER=$this->sanitizePrivate($_SERVER, $badwords);
$_ENV=$this->sanitizePrivate($_ENV, $badwords);

object to json_encode return false in linux server cakephp 3

I'm facing a horrible problem in Linux server. I don't understand what to do now.
My problem is that in Linux server my script runs properly without any error but when I use an object to JSON encode it returns false.
Please any one help. Thanks in advance.
public function ajaxDataSearch() {
$this->loadModel('ViewDocketHistorys');
$this->render(false);
$this->viewBuilder()->layout('false');
if ($this->request->is(['post'])) {
$DocketNo = $this->request->data['DocketNo'];
$SearchData = $this->ViewDocketHistorys->find()
->where(['DocketNo' => $DocketNo])
->last();
$Jsondate = json_encode($SearchData);
echo $Jsondate;
}
}
When I debug query data
debug($SearchData); exit;
output:
object(App\Model\Entity\ViewDocketHistory) {
'MasterID' => (int) 311,
'DocketNo' => 'fhfghfghf',
'[new]' => false,
'[accessible]' => [
'*' => true
],
'[dirty]' => [],
'[original]' => [],
'[virtual]' => [],
'[errors]' => [],
'[invalid]' => [],
'[repository]' => 'ViewDocketHistorys'}
When I debug JSON encode
debug($Jsondate); exit;
Output:
false
Last found a solution When I create a view with a UNION, i have to make sure the data types of the corresponding columns are the same (or at least, similar enough for one to be converted to the other). In the current case, the first column of the view is a 'column1', and no data type definition apart from BLOB can make much sense of that.
If you really need this as a view, you could try...
SELECT e.ID AS ID,
NULL AS table_field,
...etc...
'e' AS type
FROM table1 AS e
UNION ALL
SELECT
NULL AS ID,
k.table_field AS table_field,
...etc...
'k' AS type
FROM table2 AS k;
Details here
You can try by doing:
$Jsondate = json_encode($SearchData->toArray());
->toArray() will convert entity class into a clean array, after that, json_decode would receive the right structure to JavaScript Object Notation.

Put string content into array php

I have this string
string(2091) "
"roots" => array(
array(
"driver" => "LocalFileSystem", // driver for accessing file system (REQUIRED)
"path" => "../files/", // path to files (REQUIRED)
//"URL" => dirname($_SERVER["PHP_SELF"]) . "/../files/", // URL to files (REQUIRED)
"accessControl" => "access" , // disable and hide dot starting files (OPTIONAL)
"alias" => "Root",
//"uploadDeny" => array("all"),
"attributes" => array(
array(
"pattern" => "/\manuali$/", //You can also set permissions for file types by adding, for example, <b>.jpg</b> inside pattern.
// "pattern" =>"/\PROVA$/",
"read" => true,
"write" => false,
"locked" => true,
),
array(
"pattern" => "/rapporti$/", //You can also set permissions for file types by adding, for example, <b>.jpg</b> inside pattern.
// "pattern" =>"/\PROVA$/",
"read" => true,
"write" => true,
"locked" => true,
)
[...]
";
i want to put into array the entire value of a string. Ex:
array(
"roots" => array(
array(
"driver" => "LocalFileSystem", // driver for accessing file system (REQUIRED)
"path" => "../files/", // path to files (REQUIRED)
//"URL" => dirname($_SERVER["PHP_SELF"]) . "/../files/", // URL to files (REQUIRED)
"accessControl" => "access" , // disable and hide dot starting files (OPTIONAL)
"alias" => "Root",
//"uploadDeny" => array("all"),
"attributes" => array(
array(
"pattern" => "/\manuali$/", //You can also set permissions for file types by adding, for example, <b>.jpg</b> inside pattern.
// "pattern" =>"/\PROVA$/",
"read" => true,
"write" => false,
"locked" => true,
),
array(
"pattern" => "/rapporti$/", //You can also set permissions for file types by adding, for example, <b>.jpg</b> inside pattern.
// "pattern" =>"/\PROVA$/",
"read" => true,
"write" => true,
"locked" => true,
)
[...]
);
i have tried str_split() implode() array_push()... but every function put the string into array in this mode array(string(" "))i want to put in this mode array(" ").
Thanks
If your string is parseable and you don't mind about possible injections, you can simply eval your array representation.
For example, if your string is in $arrayStr and you want to create an array $myArray from that:
$arrayStr = '"roots" => array ( ... )';
eval('$myArray = array(' . $arrayStr . ');');
Note that if $arrayStr is not entirely controller by you from generation to conversion, it is highly risky since anything in it would be evaluated.
A better solution to exchange a whole array between parts of your application would be to serialize() it beforehand to create a string representation of your array/object which can be stored, and then unserialize it when you need it in its original form.
There's two main methods I can think of.
One, is to use some sort of complex pattern matching loop using preg_match. You can split the string into each line by using explode and \n to convert it into an array (of each line), loop through each row, and use pattern matching (regex) to figure out what to do with it.
The second method is incredibly dangerous, so I highly advise you to not use it, but php has it's own eval function which would let PHP interpret the string as actual PHP.
You would need to append a variable assignment inside the string e.g.
$arrayString = "$var = array(' . $arrayString . ');";
And then
eval($arrayString);
However, you really don't want to be doing this if you can. From the PHP docs:
Caution
The eval() language construct is very dangerous because it allows
execution of arbitrary PHP code. Its use thus is discouraged. If you
have carefully verified that there is no other option than to use this
construct, pay special attention not to pass any user provided data
into it without properly validating it beforehand.

Categories