PHP function to edit configuration file - php

I'm looking for a PHP function that I can use to edit key/value pairs in a text file.
What I want to do (PHP):
changeValue(key, bar);
and have in setings.txt:
key = foo
key2 =foo2
Change to:
key = bar
key2 = foo2
What I got so far (not working):
function changeValue($input) {
$file = file_get_contents('/path/to/settings.txt');
preg_match('/\b$input[0]\b/', $file, $matches);
$file = str_replace($matches[1], $input, $file);
file_put_contents('/path/to/settings.txt', $file);
}
How to update an ini file with php? got me started. I read many other questions but I couldn't get it working.

I would use JSON with at least the JSON_PRETTY_PRINT option to write and json_decode() to read.
// read file into an array of key => foo
$settings = json_decode(file_get_contents('/path/to/settings.txt'), true);
// write array to file as JSON
file_put_contents('/path/to/settings.txt', json_encode($settings, JSON_PRETTY_PRINT));
Which will create a file such as:
{
"key": "foo",
"key2": "bar",
"key3": 1
}
Another possibility is var_export() using a similar approach, or another simple example for what you're asking:
// read file into an array of key => foo
$string = implode('&', file('/path/to/settings.txt', FILE_IGNORE_NEW_LINES));
parse_str($string, $settings);
// write array to file as key=foo
$data = implode("\n", $settings);
file_put_contents('/path/to/settings.txt', $data);
So read in the file, change the setting $setting['key'] = 'bar'; and then write it out.

Instead of using file_get_contents use file, this reads each line in as an array.
Under you see working code. Had a little problem with write array added more breaks but not sure why.
changeValue("key", "test123");
function changeValue($key, $value)
{
//get each line as an array.
$file = file("test.txt");
//go through the array, the value is references so when it is changed the value in the array is changed.
foreach($file as &$val)
{
//check if the string line contains the current key. If it contains the key replace the value. substr takes everything before "=" so not to run if the value is the same as the key.
if(strpos(substr($val, 0, strpos($val, "=")), $key) !== false)
{
//clear the string
$val = substr($val, 0, strpos($val, "="));
//add the value
$val .= "= " . $value;
}
}
//send the changed array writeArray();
writeArray($file);
}
function writeArray($array)
{
$str = "";
foreach($array as $value)
{
$str .= $value . "\n";
}
//write the array.
file_put_contents('test.txt', $str);
}
?>

Related

PHP file with multiple lines to array and key with value

I have files with lines in it where i want to create array with a key and value
file1 has for example:
thisisline = aline
thisisalsoaline = oke
whereiamaline = check
file2 has
thisisline = aline
thisisalsoaline = oke
whereiamaline = checker
what i am trying to create but no luck to have a result which is :(
thisisline => aline
thisisalsoaline => oke
whereiamaline => check
i tried with explode but then it was like
[0] => thisisline = aline
the endgoal is to have 2 files to compare with array_diff_key so that i can identify the line whereiamaline = checker
Could somebody point me to the correct direction?
Thank you
Your files look like ini-files. php already has parse_ini_file function, which will return key => value array.
Next, correct function is array_diff_assoc:
$a = parse_ini_file('file1');
$b = parse_ini_string('file2');
print_r(array_diff_assoc($a, $b));
Because array_diff_key returns keys which are in first array, but not in second, which is not your case.
You can do it with explode function with delimeter =, like:
$finall_array = array();
$handle = fopen("file.txt", "r");
if ($handle) {
while (($line = fgets($handle)) !== false) {
$le = explode("=",$line);
$finall_array[$le[0]] = $le[1];
}
fclose($handle);
} else {
// here goes error file opeping
}
Then just use $final_array like your output
You can do it this way.
foreach(file("file1.txt") as $line) {
$pieces = explode("=", $line);
//Do whatever you want to do with $pieces here. $pieces[0] and $pieces[1]
//Trim the values of $pieces too.
}
Another way is:
for each(file("file1.txt") as $line) {
$res[] = array_map('trim', explode('=', $line));
}
This one will directly populate the $res[] array with arrays of each line (which are trimmed)
So i used the parse_in_file together with the arra_diff_assoc and then it is
<?php
$file1 = parse_ini_file("master_manager.txt");
$file2 = parse_ini_file("master_manager1.txt");
echo "<pre>";
$myarray = array_diff_assoc($file1, $file2);
foreach ($myarray as $key => $value){
echo $key." = ".$value."\n"; }
echo "</pre>";
but then if a line contains false or true it gives a 1 or nothing (null) how to prevent that? –

How to set .env values in laravel programmatically on the fly

I have a custom CMS that I am writing from scratch in Laravel and want to set env values i.e. database details, mailer details, general configuration, etc from controller once the user sets up and want to give user the flexibility to change them on the go using the GUI that I am making.
So my question is how do I write the values received from user to the .env file as an when I need from the controller.
And is it a good idea to build the .env file on the go or is there any other way around it?
Since Laravel uses config files to access and store .env data, you can set this data on the fly with config() method:
config(['database.connections.mysql.host' => '127.0.0.1']);
To get this data use config():
config('database.connections.mysql.host')
To set configuration values at runtime, pass an array to the config helper
https://laravel.com/docs/5.3/configuration#accessing-configuration-values
Watch out! Not all variables in the laravel .env are stored in the config environment.
To overwrite real .env content use simply:
putenv ("CUSTOM_VARIABLE=hero");
To read as usual, env('CUSTOM_VARIABLE') or env('CUSTOM_VARIABLE', 'devault')
NOTE: Depending on which part of your app uses the env setting, you may need to set the variable early by placing it into your index.php or bootstrap.php file. Setting it in your app service provider may be too late for some packages/uses of the env settings.
More simplified:
public function putPermanentEnv($key, $value)
{
$path = app()->environmentFilePath();
$escaped = preg_quote('='.env($key), '/');
file_put_contents($path, preg_replace(
"/^{$key}{$escaped}/m",
"{$key}={$value}",
file_get_contents($path)
));
}
or as helper:
if ( ! function_exists('put_permanent_env'))
{
function put_permanent_env($key, $value)
{
$path = app()->environmentFilePath();
$escaped = preg_quote('='.env($key), '/');
file_put_contents($path, preg_replace(
"/^{$key}{$escaped}/m",
"{$key}={$value}",
file_get_contents($path)
));
}
}
Based on josh's answer. I needed a way to replace the value of a key inside the .env file.
But unlike josh's answer, I did not want to depend on knowing the current value or the current value being accessible in a config file at all.
Since my goal is to replace values that are used by Laravel Envoy which doesn't use a config file at all but instead uses the .env file directly.
Here's my take on it:
public function setEnvironmentValue($envKey, $envValue)
{
$envFile = app()->environmentFilePath();
$str = file_get_contents($envFile);
$oldValue = strtok($str, "{$envKey}=");
$str = str_replace("{$envKey}={$oldValue}", "{$envKey}={$envValue}\n", $str);
$fp = fopen($envFile, 'w');
fwrite($fp, $str);
fclose($fp);
}
Usage:
$this->setEnvironmentValue('DEPLOY_SERVER', 'forge#122.11.244.10');
Based on totymedli's answer.
Where it is required to change multiple enviroment variable values at once, you could pass an array (key->value). Any key not present previously will be added and a bool is returned so you can test for success.
public function setEnvironmentValue(array $values)
{
$envFile = app()->environmentFilePath();
$str = file_get_contents($envFile);
if (count($values) > 0) {
foreach ($values as $envKey => $envValue) {
$str .= "\n"; // In case the searched variable is in the last line without \n
$keyPosition = strpos($str, "{$envKey}=");
$endOfLinePosition = strpos($str, "\n", $keyPosition);
$oldLine = substr($str, $keyPosition, $endOfLinePosition - $keyPosition);
// If key does not exist, add it
if (!$keyPosition || !$endOfLinePosition || !$oldLine) {
$str .= "{$envKey}={$envValue}\n";
} else {
$str = str_replace($oldLine, "{$envKey}={$envValue}", $str);
}
}
}
$str = substr($str, 0, -1);
if (!file_put_contents($envFile, $str)) return false;
return true;
}
#more simple way to cover .env you can do like this
$_ENV['key'] = 'value';
tl;dr
Based on vesperknight's answer I created a solution that doesn't use strtok or env().
private function setEnvironmentValue($envKey, $envValue)
{
$envFile = app()->environmentFilePath();
$str = file_get_contents($envFile);
$str .= "\n"; // In case the searched variable is in the last line without \n
$keyPosition = strpos($str, "{$envKey}=");
$endOfLinePosition = strpos($str, PHP_EOL, $keyPosition);
$oldLine = substr($str, $keyPosition, $endOfLinePosition - $keyPosition);
$str = str_replace($oldLine, "{$envKey}={$envValue}", $str);
$str = substr($str, 0, -1);
$fp = fopen($envFile, 'w');
fwrite($fp, $str);
fclose($fp);
}
Explanation
This doesn't use strtok that might not work for some people, or env() that won't work with double-quoted .env variables which are evaluated and also interpolates embedded variables
KEY="Something with spaces or variables ${KEY2}"
If you don't need to save your changes to .env file, you could simply user this code :
\Illuminate\Support\Env::getRepository()->set('APP_NAME','New app name');
In the event that you want these settings to be persisted to the environment file so they be loaded again later (even if the configuration is cached), you can use a function like this. I'll put the security caveat in there, that calls to a method like this should be gaurded tightly and user input should be sanitized properly.
private function setEnvironmentValue($environmentName, $configKey, $newValue) {
file_put_contents(App::environmentFilePath(), str_replace(
$environmentName . '=' . Config::get($configKey),
$environmentName . '=' . $newValue,
file_get_contents(App::environmentFilePath())
));
Config::set($configKey, $newValue);
// Reload the cached config
if (file_exists(App::getCachedConfigPath())) {
Artisan::call("config:cache");
}
}
An example of it's use would be;
$this->setEnvironmentValue('APP_LOG_LEVEL', 'app.log_level', 'debug');
$environmentName is the key in the environment file (example.. APP_LOG_LEVEL)
$configKey is the key used to access the configuration at runtime (example.. app.log_level (tinker config('app.log_level')).
$newValue is of course the new value you wish to persist.
If you want to change it temporarily (e.g. for a test), this should work for Laravel 8:
<?php
namespace App\Helpers;
use Dotenv\Repository\Adapter\ImmutableWriter;
use Dotenv\Repository\AdapterRepository;
use Illuminate\Support\Env;
class DynamicEnvironment
{
public static function set(string $key, string $value)
{
$closure_adapter = \Closure::bind(function &(AdapterRepository $class) {
$closure_writer = \Closure::bind(function &(ImmutableWriter $class) {
return $class->writer;
}, null, ImmutableWriter::class);
return $closure_writer($class->writer);
}, null, AdapterRepository::class);
return $closure_adapter(Env::getRepository())->write($key, $value);
}
}
Usage:
App\Helpers\DynamicEnvironment::set("key_name", "value");
Based on totymedli's answer and Oluwafisayo's answer.
I set a little modification to change the .env file, It works too fine in Laravel 5.8, but when after I changed it the .env file was modificated I could see variables did not change after I restart with php artisan serve, so I tried to clear cache and others but I can not see a solution.
public function setEnvironmentValue(array $values)
{
$envFile = app()->environmentFilePath();
$str = file_get_contents($envFile);
$str .= "\r\n";
if (count($values) > 0) {
foreach ($values as $envKey => $envValue) {
$keyPosition = strpos($str, "$envKey=");
$endOfLinePosition = strpos($str, "\n", $keyPosition);
$oldLine = substr($str, $keyPosition, $endOfLinePosition - $keyPosition);
if (is_bool($keyPosition) && $keyPosition === false) {
// variable doesnot exist
$str .= "$envKey=$envValue";
$str .= "\r\n";
} else {
// variable exist
$str = str_replace($oldLine, "$envKey=$envValue", $str);
}
}
}
$str = substr($str, 0, -1);
if (!file_put_contents($envFile, $str)) {
return false;
}
app()->loadEnvironmentFrom($envFile);
return true;
}
So it rewrites correctly the .env file with the funtion setEnvironmentValue, but How can Laravel reload the new .env without to restart the system?
I was looking information about that and I found
Artisan::call('cache:clear');
but in local it does not work! for me, but when I uploaded the code and test in my serve it works to fine.
I tested it in Larave 5.8 and worked in my serve...
This could be a tip when you have a variable with more than one word and separetly with a space, the solution i did
public function update($variable, $value)
{
if ($variable == "APP_NAME" || $variable == "MAIL_FROM_NAME") {
$value = "\"$value\"";
}
$values = array(
$variable=>$value
);
$this->setEnvironmentValue($values);
Artisan::call('config:clear');
return true;
}
Tailor Otwell generate the laravel application key and set its value using this code (code modified for example purpose):
$escaped = preg_quote('='.config('broadcasting.default'), '/');
file_put_contents(app()->environmentFilePath(), preg_replace("/^BROADCAST_DRIVER{$escaped}/m", 'BROADCAST_DRIVER='.'pusher',
file_get_contents(app()->environmentFilePath())
));
You can find the code in the key generation class:
Illuminate\Foundation\Console\KeyGenerateCommand
you can use this package
https://github.com/ImLiam/laravel-env-set-command
then use Artisan Facade to call artisan commands ex:
Artisan::call('php artisan env:set app_name Example')
PHP 8 solution
This solution is using env() so should only be run when the configuration is NOT cached.
/**
* #param string $key
* #param string $value
*/
protected function setEnvValue(string $key, string $value)
{
$path = app()->environmentFilePath();
$env = file_get_contents($path);
$old_value = env($key);
if (!str_contains($env, $key.'=')) {
$env .= sprintf("%s=%s\n", $key, $value);
} else if ($old_value) {
$env = str_replace(sprintf('%s=%s', $key, $old_value), sprintf('%s=%s', $key, $value), $env);
} else {
$env = str_replace(sprintf('%s=', $key), sprintf('%s=%s',$key, $value), $env);
}
file_put_contents($path, $env);
}
this function update new value of existing key or add new key=value to end of file
function setEnv($envKey, $envValue) {
$path = app()->environmentFilePath();
$escaped = preg_quote('='.env($envKey), '/');
//update value of existing key
file_put_contents($path, preg_replace(
"/^{$envKey}{$escaped}/m",
"{$envKey}={$envValue}",
file_get_contents($path)
));
//if key not exist append key=value to end of file
$fp = fopen($path, "r");
$content = fread($fp, filesize($path));
fclose($fp);
if (strpos($content, $envKey .'=' . $envValue) == false && strpos($content, $envKey .'=' . '\"'.$envValue.'\"') == false){
file_put_contents($path, $content. "\n". $envKey .'=' . $envValue);
}
}
$envFile = app()->environmentFilePath();
$_ENV['APP_NAME'] = "New Project Name";
$txt= "";
foreach ($_ENV as $key => $value) {
$txt .= $key."=".$value."\n"."\n";
}
file_put_contents($envFile,$txt);
This solution can create, replace and save the values given in the .env file.
I created this to be used in a command package:install to add the .env key-value pairs automatically to the .env file.
/**
* Set .env key-value pair
*
* #param array $keyPairs Desired key name
* #return void
*/
public function saveArrayToEnv(array $keyPairs)
{
$envFile = app()->environmentFilePath();
$newEnv = file_get_contents($envFile);
$newlyInserted = false;
foreach ($keyPairs as $key => $value) {
// Make sure key is uppercase (can be left out)
$key = Str::upper($key);
if (str_contains($newEnv, "$key=")) {
// If key exists, replace value
$newEnv = preg_replace("/$key=(.*)\n/", "$key=$value\n", $newEnv);
} else {
// Check if spacing is correct
if (!str_ends_with($newEnv, "\n\n") && !$newlyInserted) {
$newEnv .= str_ends_with($newEnv, "\n") ? "\n" : "\n\n";
$newlyInserted = true;
}
// Append new
$newEnv .= "$key=$value\n";
}
}
$fp = fopen($envFile, 'w');
fwrite($fp, $newEnv);
fclose($fp);
}
You pass it an associative array:
$keyPairs = [
'KEY' => 'VALUE',
'API_KEY' => 'XXXXXX-XXXXXX-XXXXX'
]
And it will add them or replace they values of the given keys with the given values.
Of course there is room for improvement like checking if the array is actually an associative array. But this will do for hard coded stuff in things like Laravel command classes.
This solution builds upon the one provided by Elias Tutungi, it accepts multiple value changes and uses a Laravel Collection because foreach's are gross
function set_environment_value($values = [])
{
$path = app()->environmentFilePath();
collect($values)->map(function ($value, $key) use ($path) {
$escaped = preg_quote('='.env($key), '/');
file_put_contents($path, preg_replace(
"/^{$key}{$escaped}/m",
"{$key}={$value}",
file_get_contents($path)
));
});
return true;
}
You can use this custom method i located from internet ,
/**
* Calls the method
*/
public function something(){
// some code
$env_update = $this->changeEnv([
'DB_DATABASE' => 'new_db_name',
'DB_USERNAME' => 'new_db_user',
'DB_HOST' => 'new_db_host'
]);
if($env_update){
// Do something
} else {
// Do something else
}
// more code
}
protected function changeEnv($data = array()){
if(count($data) > 0){
// Read .env-file
$env = file_get_contents(base_path() . '/.env');
// Split string on every " " and write into array
$env = preg_split('/\s+/', $env);;
// Loop through given data
foreach((array)$data as $key => $value){
// Loop through .env-data
foreach($env as $env_key => $env_value){
// Turn the value into an array and stop after the first split
// So it's not possible to split e.g. the App-Key by accident
$entry = explode("=", $env_value, 2);
// Check, if new key fits the actual .env-key
if($entry[0] == $key){
// If yes, overwrite it with the new one
$env[$env_key] = $key . "=" . $value;
} else {
// If not, keep the old one
$env[$env_key] = $env_value;
}
}
}
// Turn the array back to an String
$env = implode("\n", $env);
// And overwrite the .env with the new data
file_put_contents(base_path() . '/.env', $env);
return true;
} else {
return false;
}
}
use the function below to change values in .env file laravel
public static function envUpdate($key, $value)
{
$path = base_path('.env');
if (file_exists($path)) {
file_put_contents($path, str_replace(
$key . '=' . env($key), $key . '=' . $value, file_get_contents($path)
));
}
}
Replace single value in .env file function:
/**
* #param string $key
* #param string $value
* #param null $env_path
*/
function set_env(string $key, string $value, $env_path = null)
{
$value = preg_replace('/\s+/', '', $value); //replace special ch
$key = strtoupper($key); //force upper for security
$env = file_get_contents(isset($env_path) ? $env_path : base_path('.env')); //fet .env file
$env = str_replace("$key=" . env($key), "$key=" . $value, $env); //replace value
/** Save file eith new content */
$env = file_put_contents(isset($env_path) ? $env_path : base_path('.env'), $env);
}
Example to local (laravel) use: set_env('APP_VERSION', 1.8).
Example to use custom path: set_env('APP_VERSION', 1.8, $envfilepath).

Convert a semicolon separated string to an array of associative arrays then json encode

I am facing some problems while converting a set of semicolon delimited strings to json.
The input string:
si;dialed_no;connect_time;duration;region;call_cost
0;918592877727;2015-08-25 18:51:01;21;India(91);0.029
1;918907777727;2015-08-25 19:04:08;220;India(91);0.232
2;918907777727;2015-08-25 19:09:50;40;India(91);0.058
3;918907777727;2015-08-25 19:10:46;69;India(91);0.087
4;919048232151;2015-08-26 13:30:24;19;India(91);0.029
5;919895842822;2015-08-26 14:23:35;423;India(91);0.435
My code:
function my_wrap($val) {
return '{"test":"' . $val. '"}';
}
$parts = explode(';', $string);
$parts = array_map('my_wrap', $parts);
$json = '[' . implode(',', $parts) . ']';
echo $json;
And the output is like:
[{"test":"dialed_no"},{"test":"connect_time"},{"test":"duration"},{"test":"region"},{"test":"call_cost 0"},{"test":"918592877727"},{"test":"2015-08-25 18:51:01"},{"test":"21"},{"test":"India(91)"},{"test":"0.029 1"},{"test":"918907777727"},{"test":"2015-08-25 19:04:08"},{"test":"220"},{"test":"India(91)"},{"test":"0.232 2"},{"test":"918907777727"},{"test":"2015-08-25 19:09:50"},{"test":"40"},{"test":"India(91)"},{"test":"0.058 3"},{"test":"918907777727"},{"test":"2015-08-25 19:10:46"},{"test":"69"},{"test":"India(91)"},{"test":"0.087 4"},{"test":"919048232151"},{"test":"2015-08-26 13:30:24"},{"test":"19"},{"test":"India(91)"},{"test":"0.029 5"},{"test":"919895842822"},{"test":"2015-08-26 14:23:35"},{"test":"423"},{"test":"India(91)"},{"test":"0.435 6"},{"test":"8801711788025"},{"test":"2015-08-30 19:29:48"},{"test":"1"},{"test":"Bangladesh(880)"},{"test":"0.029 7"},{"test":"8801711788025"},{"test":"2015-08-30 19:29:57"},{"test":"2"},{"test":"Bangladesh(880)"},{"test":"0.029 8"},{"test":"8801711788025"},{"test":"2015-08-30 19:30:07"},{"test":"2"},{"test":"Bangladesh(880)"},{"test":"0.029 9"},{"test":"8801711788025"},{"test":"2015-08-30 19:30:17"},{"test":"1"},{"test":"Bangladesh(880)"},{"test":"0.029 10"},{"test":"8801711788025"},{"test":"2015-08-30 21:24:31"},{"test":"88"},{"test":"Bangladesh(880)"},{"test":"0.087 11"},{"test":"8801833316038"},{"test":"2015-08-31 12:06:15"},{"test":"5"},{"test":"Bangladesh(880)"},{"test":"0.029 12"}]
What I want is like:
[{si:"0",dialed_no:"91xxx",connect_time:"2015-08-25 18:51:01"}, {si:"1",dialed_no:"9184sd",connect_time:"2015-08-25 18:51:01"}]
and so on...
Note: I am getting the above input string from a API URL and not from a csv file or something.
Can you try this code and see if it works as you want?
//we split the single lines
$lines = explode("\n", $string);
$linesArray = array();
//we split each line in a set of elements
foreach($lines as $line){
$linesArray[] = explode(";",$line);
}
//we use the first line of data as an array of headers
$headers = $linesArray[0];
//and remove it
unset($linesArray[0]);
$jsonArray = [];
foreach($linesArray as $l=>$ln){
foreach($ln as $k=>$part){
//we re-build an array with the right headers
$jsonArray[$l][$headers[$k]] = $part;
}
}
print json_encode($jsonArray);
What you described as the output you want is not JSON.
Don't invent your own routines when PHP already has perfectly good ones (e.g. for JSON encoding and CSV parsing).
Assuming that the data is starting in a file....
$data=array();
$y=0;
$header=fgtetcsv($file_handle, 0, ';');
while (!feof($file_handle)) {
$row=fgtetcsv($file_handle, 0, ';');
foreach ($row as $x=>$value) {
$data[$y][$header[$x]]=$value;
}
$y++;
}
print json_encode($data);
Of course this will need some tweaks to handle error conditions and possibly for datasets larger than the working memory of php.
<?php
$records = array_map(
function($e) { // 2: to each line/record apply this functions
return str_getcsv($e, ';'); // 3: split the line/record into fields
},
explode( "\n", data() ) // 1: split data into "lines"/records
);
// 4: now $records is an array of records, each being an array of fields
$fields = array_shift($records); // 5: first record contains the field names, remove from array and assign to $fields
$records = array_map(
function($e) use ($fields) { // 7: this function has access to $fields, i.e. the names of the fields
return array_combine($fields, $e); // 8: see http://docs.php.net/array_combine
},
$records // 6: apply the function above to each element, i.e. record, in $records
);
echo json_encode($records);
function data() {
return <<< eot
si;dialed_no;connect_time;duration;region;call_cost
0;918592877727;2015-08-25 18:51:01;21;India(91);0.029
1;918907777727;2015-08-25 19:04:08;220;India(91);0.232
2;918907777727;2015-08-25 19:09:50;40;India(91);0.058
3;918907777727;2015-08-25 19:10:46;69;India(91);0.087
4;919048232151;2015-08-26 13:30:24;19;India(91);0.029
5;919895842822;2015-08-26 14:23:35;423;India(91);0.435
eot;
}
While I appreciate that VolkerK is using a lot of the right functions, I find that functional syntax creates too much bloat and makes the code harder to read. Furthermore, this task can be accomplished in a single loop and therefore it should be.
Code: (Demo)
$input = <<<SSV
si;dialed_no;connect_time;duration;region;call_cost
0;918592877727;2015-08-25 18:51:01;21;India(91);0.029
1;918907777727;2015-08-25 19:04:08;220;India(91);0.232
2;918907777727;2015-08-25 19:09:50;40;India(91);0.058
3;918907777727;2015-08-25 19:10:46;69;India(91);0.087
4;919048232151;2015-08-26 13:30:24;19;India(91);0.029
5;919895842822;2015-08-26 14:23:35;423;India(91);0.435
SSV;
$lines = explode(PHP_EOL, $input);
$header = str_getcsv(array_shift($lines), ';');
foreach ($lines as $line) {
$result[] = array_combine($header, str_getcsv($line, ';'));
}
echo json_encode($result, JSON_PRETTY_PRINT);
A note to the OP, you must not manually craft a json string. Always rely on the accuracy of json_encode() -- it won't fail you.

How do I search a file and return an array of the results and line number in PHP?

How do I search a file and return an array of the results so that I can use it in a collection in PHP?
So, for example, say I have a .txt file with something like:
hellohello
hihi
heywhats up
hello hey whats up
hello
And I want to search for all occurrences with hello and its line number, then return it as an array so I can use it in a data collector.
So, it would return the line number and line, like:
$results = array
(
array('1', 'hellohello'),
array('4', 'hello hey whats up'),
array('5', 'hello'),
);
My idea is to us file_get_contents.
So, for example..
$file = 'example.txt';
function get_file($file) {
$file = file_get_contents($file);
return $file;
}
function searchFile($search_str) {
$matches = preg_match('/$search_str/i', get_file($file);
return $matches;
}
As an alternative, you could also use file() function so it reads the entire file into an array. Then you could just loop, then search. Rough example:
$file = 'example.txt';
$search = 'hello';
$results = array();
$contents = file($file);
foreach($contents as $line => $text) {
if(stripos($text, $search) !== false) {
$results[] = array($line+1, $text);
}
}
print_r($results);
Sidenote: stripos() is just an example, you could still use your other way/preference to search the needle for that particular line.

Convert CSV to JSON using PHP

I am trying to convert CSV file to JSON using PHP.
Here is my code
<?php
date_default_timezone_set('UTC');
$today = date("n_j"); // Today is 1/23/2015 -> $today = 1_23
$file_name = $today.'.CSV'; // My file name is 1_23.csv
$file_path = 'C:\\Users\\bheng\\Desktop\\qb\\'.$file_name;
$file_handle = fopen($file_path, "r");
$result = array();
if ($file_handle !== FALSE) {
$column_headers = fgetcsv($file_handle);
foreach($column_headers as $header) {
$result[$header] = array();
}
while (($data = fgetcsv($file_handle)) !== FALSE) {
$i = 0;
foreach($result as &$column) {
$column[] = $data[$i++];
}
}
fclose($file_handle);
}
// print_r($result); // I see all data(s) except the header
$json = json_encode($result);
echo $json;
?>
print_r($result); // I see all data(s)
Then I json_encode($result); and tried to display it, but nothing is displaying on the screen at all. All I see is the blank screen, and 0 error message.
Am I doing anything wrong ? Can someone help me ?
Added Result of print_r($result);
Array (
[Inventory] => Array (
[0] => bs-0468R(20ug)
[1] => bs-1338R(1ml)
[2] => bs-1557G(no bsa)
[3] => bs-3295R(no BSA)
[4] => bs-0730R-Cy5"
[5] => bs-3889R-PE-Cy7"
[6] => 11033R
[7] => 1554R-A647
[8] => 4667
[9] => ABIN731018
[10] => Anti-DBNL protein
.... more ....
Try like this:
$file="1_23.csv";
$csv= file_get_contents($file);
$array = array_map("str_getcsv", explode("\n", $csv));
$json = json_encode($array);
print_r($json);
data.csv
Game,Skill
Treasure Hunter,pilipala
Rocket Launcher,bibobibo
Rocket Engine,hehehohoho
To convert with column name, this is how I do it.
csv2json.php
<?php
if (($handle = fopen("data.csv", "r")) !== FALSE) {
$csvs = [];
while(! feof($handle)) {
$csvs[] = fgetcsv($handle);
}
$datas = [];
$column_names = [];
foreach ($csvs[0] as $single_csv) {
$column_names[] = $single_csv;
}
foreach ($csvs as $key => $csv) {
if ($key === 0) {
continue;
}
foreach ($column_names as $column_key => $column_name) {
$datas[$key-1][$column_name] = $csv[$column_key];
}
}
$json = json_encode($datas);
fclose($handle);
print_r($json);
}
The output result
[
{
"Game": "Treasure Hunter",
"Skill": "pilipala"
},
{
"Game": "Rocket Launcher",
"Skill": "bibobibo"
},
{
"Game": "Rocket Engine",
"Skill": "hehehohoho"
}
]
You can try this way too.
<?php
function csvtojson($file,$delimiter)
{
if (($handle = fopen($file, "r")) === false)
{
die("can't open the file.");
}
$csv_headers = fgetcsv($handle, 4000, $delimiter);
$csv_json = array();
while ($row = fgetcsv($handle, 4000, $delimiter))
{
$csv_json[] = array_combine($csv_headers, $row);
}
fclose($handle);
return json_encode($csv_json);
}
$jsonresult = csvtojson("./doc.csv", ",");
echo $jsonresult;
I ran into a similar problem, I ended up using this to recursively convert the data to UTF-8 on an array before encoding to JSON.
function utf8_converter($array)
{
array_walk_recursive($array, function(&$item, $key){
if(!mb_detect_encoding($item, 'utf-8', true)){
$item = utf8_encode($item);
}
});
return $array;
}
From:
http://nazcalabs.com/blog/convert-php-array-to-utf8-recursively/
This issue is pretty old by now, but hoping this helps someone, as it seemed like the simplest example I found, and I know this is a pretty common thing devs might need to do as a beginner, and lots of answers gloss over the magic.
$file = storage_path('app/public/waitlist_users_test.csv'); //--> laravel helper, but you can use any path here
function csv_to_json($file)
{
// file() loads each row as an array value, then array map uses the 'str_getcsv' callback to
$csv = array_map('str_getcsv', file($file));
// array_walk - "walks" through each item of the array and applies the call back function. the & in "&row" means that alterations to $row actually change the original $csv array, rather than treating it as immutable (*sort of immutable...)
array_walk($csv, function(&$row) use ($csv) {
// array_combine takes the header row ($csv[0]) and uses it as array keys for each column in the row
$row = array_combine($csv[0], $row);
});
array_shift($csv); # removes now very redundant column header --> contains {'col_1':'col_1', 'col_2':'col_2'...}
$json = json_encode($csv);
return $json;
}
There's a lot of magic going on with these functions that accept callback functions, that didn't seem to be explained thoroughly above. I'm self taught and have been programming for years, and find that it's often just glossed over without detailing how callbacks work, so I'll dive in just a little bit for the array_map('str_getcsv', file($file)) function - if you pass a function you've written, or inbuilt php function name as a string, it will take the value of whatever (in this case - array) element is being evaluated by the calling function (in this case array_map), and pass that to the callback function without the need to explicitly pass in a variable - super helpful once you get the hang of it, but I find it's not explained thoroughly very often which leaves beginners to not understand why it works, just that it works.
I've linked most of these above, but here's a little more information:
str-getcsv do? Array Walk Array Map Callables/Callbacks
as #MoonCactus noted, the file() function only loads 1 row at a time which helps save on memory usage for large .csv files.
Also, some other posts reference using explode - why not use explode() instead of str_getcsv() to parse rows? Because explode() would not treat possible enclosured parts of string or escaped characters correctly.
Hope somebody finds this helpful!
If you are converting a dynamic CSV file, you can pass the URL through a parameter (url=http://example.com/some.csv) and it will show you the most up-to-date version:
<?php
// Lets the browser and tools such as Postman know it's JSON
header( "Content-Type: application/json" );
// Get CSV source through the 'url' parameter
if ( isset( $_GET['url'] ) ) {
$csv = explode( "\n", file_get_contents( $_GET['url'] ) );
$index = str_getcsv( array_shift( $csv ) );
$json = array_map(
function ( $e ) use ( $index ) {
return array_combine( $index, str_getcsv( $e ) );
}, $csv
);
}
else {
$json = "Please set the path to your CSV by using the '?url=' query string.";
}
// Output JSON
echo json_encode( $json );
Alternate solution that uses similar method as #Whirlwind's solution but returns a more standard JSON result (with named fields for each object/record):
// takes a string of CSV data and returns a JSON representing an array of objects (one object per row)
function convert_csv_to_json($csv_data){
$flat_array = array_map("str_getcsv", explode("\n", $csv_data));
// take the first array item to use for the final object's property labels
$columns = $flat_array[0];
for ($i=1; $i<count($flat_array)-1; $i++){
foreach ($columns as $column_index => $column){
$obj[$i]->$column = $flat_array[$i][$column_index];
}
}
$json = json_encode($obj);
return $json; // or just return $obj if that's a more useful return value
}
The accepted answer uses file_get_contents() to read the entire file as a string in memory, and then explode() it to make it an array.
But it can be made faster, smaller in memory, and more useful:
function ReadCsv($fn)
{
$lines= file($fn); // read file directly as an array of lines
array_pop($lines); // you can remove the last empty line (if required)
$json= json_encode(array_map("str_getcsv", $lines), JSON_NUMERIC_CHECK);
print_r($json);
}
Nb: I used JSON_NUMERIC_CHECK here to avoid numbers being double quoted into strings. It also reduces the output size and it usually helps javascript on the other side (e.g. to compute or plot the data). Beware of phone numbers though!
I liked #ian-d-miller's solution for converting the data into a key / value style format, but I kept running into issues with his code.
Here's what worked for me:
function convert_CSV_to_JSON($csv_data){
// convert csv data to an array
$data = array_map("str_getcsv", explode("\n", $csv_data));
// use the first row as column headers
$columns = $data[0];
// create array to hold our converted data
$json = [];
// iterate through each row in the data
foreach ($data as $row_index => $row_data) {
// skip the first row, since it's the headers
if($row_index === 0) continue;
// make sure we establish each new row as an array
$json[$row_index] = [];
// iterate through each column in the row
foreach ($row_data as $column_index => $column_value) {
// get the key for each entry
$label = $columns[$column_index];
// add this column's value to this row's index / column's key
$json[$row_index][$label] = $column_value;
}
}
// bam
return $json;
}
Usage:
// as is
$json = convert_CSV_to_JSON($csv);
// encoded
$json = json_encode($json);
Something that i've made for myself and may be useful for others :)
This will convert CSV into JSON array with objects (key => value pair).
function csv2json($a, $e = true) {
$b = ["\r\n","\r","\n",];
foreach ($b as $c => $d) {
$a = explode($d, $a);
$a = isset($b[$c + 1]) ? implode($b[$c + 1], $a) : implode(PHP_EOL, $a);
}
// Convert to CSV
$a = array_map("str_getcsv", explode(PHP_EOL, $a));
// Get the first part of the array as the keys
$a = [
"keys" => array_shift($a),
"rows" => $a,
"row" => null,
];
// Define JSON
$b = [];
foreach ($a["rows"] as $a["row"]) {
$a["row"] = [ "csv" => $a["row"], "json" => (object)[], ];
for ($c = 0; $c < count($a["row"]["csv"]); $c++) {
$a["row"]["csv"][$c] = [#json_decode($a["row"]["csv"][$c]),$a["row"]["csv"][$c]];
// Switch from string to booleans, numbers and others
$a["row"]["csv"][$c] = isset($a["row"]["csv"][$c][0]) ? $a["row"]["csv"][$c][0] : $a["row"]["csv"][$c][1];
// Push it back
$a["row"]["json"]->{$a["keys"][$c]} = $a["row"]["csv"][$c];
}
$a["row"] = $a["row"]["json"];
$b[] = $a["row"];
unset($a["row"]);
}
// $e will be "return"
$e = $e ? json_encode($b) : $b;
// Unset useless variables
unset($a, $b, $c, $d);
return $e;
}
How to use?
If you want to return the JSON as a string, Leave it as default.
If you want to return the JSON as an object / array, set the second parameter to false.
Examples:
$csv = "name,age,gender
John Doe,35,male
Jane Doe,32,female";
echo csv2json($csv, true); // Or without the second parameter, just csv2json($csv)
The example above (^) will return a JSON stringified, Like this:
[{"name":"John Doe","age":35,"gender":"male"},{"name":"Jane Doe","age":32,"gender":"female"}]
and the example below:
var_dump(csv2json($csv, false));
will return a JSON array with these objects:
array(2) {
[0]=>
object(stdClass)#1 (3) {
["name"]=>
string(8) "John Doe"
["age"]=>
int(35)
["gender"]=>
string(4) "male"
}
[1]=>
object(stdClass)#2 (3) {
["name"]=>
string(8) "Jane Doe"
["age"]=>
int(32)
["gender"]=>
string(6) "female"
}
}
public function CsvToJson($fileContent){
//Convert CSV To Json and Return
$all_rows = array();
$newhead =array();
//Extract csv data to array on \n
$array = explode("\n",$fileContent);
//Extract csv header to array on 0 Index
$header = explode(",",$array[0]);
//Remove Header Row From Main Data Array
array_shift($array);
//Extract All Arrays To Saperate Orders
foreach($array as $arr){
$sliced = explode(",",$arr);
array_push($all_rows,$sliced);
}
//Extract All Orders Element To Saperate Array Item
foreach($all_rows as $row){
$sliced = explode(",",$arr);
array_push($all_rows,$sliced);
}
//Remove \r From Header Elements
foreach($header as $key=>$value){
$sliced = str_replace ("\r", "", $value);
array_push($newhead,$sliced);
}
//COMBINE Header as KEY And Row Element As Value
$arrrr = array();
foreach($all_rows as $row) {
//Remove Last Element of ROW if it is \r (Break given in css file for next row)
$count= count($row);
if ($row[$count-1] == "\r") {
array_splice($row, count($row) - 1, 1);
}
//CHECK IF HADER COUNT == ROW COUNT
if (count($header) == count($row)) {
array_push($arrrr,array_combine($newhead,$row));
}
}
//CONVERT ARRAY TO JSON
$json = json_encode($arrrr);
//Remove backslasesh from json key and and value to remove \r
$clean = stripslashes($json);
//CONVERT ARRAY TO JSON AGAIN FOR EXPORT
$jsonagain = json_encode($clean);
return $jsonagain;
}

Categories