I am primarily a PHP developer and have limited experience with Perl.
I was tasked with writing a queue script in Perl which checks against a database, and that is all working out great.
The problem I have is that in the Perl script I need to include a database hostname and password.
Right now I have them hard coded, which works fine, but my PHP application uses a global PHP array which holds the database hostname and password.I'd like to be able to use this PHP array in my Perl script.
Here is my PHP array
<?php
return array(
'database' => array(
'master' => array(
'hostname' => 'fd35:4776:6804:2:a::1',
'password' => 'password'
),
'slave' => array(
'hostname' => 'fd35:4776:6804:2:2::2',
'password' => 'password',
'profile' => true
)
)
);
I've tried searching with Google and have read many random posts on line, but I have yet been able to come up with a solution.
Does anyone have any ideas which I could try? If I'm missing any additional input, let me know and I can provide it.
Edit
Hopefully I worded this properly. How would I go about including this PHP array file so that I can manipulate it with Perl?
Alternative solutions are welcome too!
You've discovered one of the many reasons why code makes for bad config files. You should move the information to an actual config file, and access that file from both that .php file and from Perl.
JSON would make a decent file format here.
{
"database": {
"master": {
"hostname": "fd35:4776:6804:2:a::1",
"password": "password"
},
"slave": {
"hostname": "fd35:4776:6804:2:2::2",
"password": "password",
"profile": true
}
}
}
The Perl code would be
use JSON::XS qw( decode_json );
open (my $fh, '<:raw', $config_path)
or die("Can't open config file $config_path: $!\n");
my $file; { local $/; $file = <$fh>; }
my $config = decode_json($file);
On the PHP side, just replace the contents of the file you showed in your post with code to read the config file. I don't know PHP, but it should be quite simple. A quick search shows it might be
return json_decode(file_get_contents($config_path));
It would be simple to provide a short PHP program that dumps the array to a file in JSON format. That file can then be read from Perl using the JSON module.
This is all that is necessary.
<?php
$array = include 'array.php';
$fh = fopen('array.json', 'w');
fwrite($fh, json_encode($array));
fclose($fh);
?>
The resultant JSON file can then be read in a Perl program, like so:
use strict;
use warnings;
use JSON 'from_json';
my $data = do {
open my $fh, '<', 'array.json' or die $!;
local $/;
from_json(<$fh>);
};
use Data::Dump;
dd $data;
output
{
database => {
master => { hostname => "fd35:4776:6804:2:a::1", password => "password" },
slave => {
hostname => "fd35:4776:6804:2:2::2",
password => "password",
profile => bless(do{\(my $o = 1)}, "JSON::XS::Boolean"),
},
},
}
There is PHP::Include, which uses a source filter to let you have PHP blocks in your Perl code to declare variables. It also has a read_file() function that applies such a filter to a single PHP file.
But it seems to expect that your PHP has assignments (e.g. $config = array('database' => array(...) and changes those to Perl variable declarations.
In a few minutes of playing with it, I couldn't get it to do anything useful with your PHP code that uses return.
If you want a more "native Perl" solution, you can pretty much* just search and replace all your "array(" and their matching ")" to "{" and "}". That'll give you a perl datastructure called a "hash of hashes" (note: Unlike PHP, Perl refers to arrays with integer indicies as arrays (and uses the # sigil to denote variables containing them), but refers to array-like things with string indicies as "hashes" (and uses the % sigil to denote variables containing them)). The Perl keywords/concepts you probably want to read up on are:
Perl Data Structures: http://perldoc.perl.org/perldsc.html
and specifically the Hash Of Hashes section: http://perldoc.perl.org/perldsc.html#HASHES-OF-HASHES
and if you dont understand what $hashref = \%hash and %hash{key} and $hashref->{key} mean in Perl, you'd want to read http://perldoc.perl.org/perlref.html
Example code (note how similar the getConfig subroutine is to your PHP code):
#!/usr/bin/perl
use strict;
use warnings;
my $config=getConfig();
print "Database master host = " . $config->{database}{master}{hostname};
print "\n";
print "Database master password = " . $config->{database}{master}{password};
print "\n";
print "Database slave profile = " . $config->{database}{slave}{profile};
print "\n";
sub getConfig{
return {
'database' => {
'master' => {
'hostname' => 'fd35:4776:6804:2:a::1',
'password' => 'password'
},
'slave' => {
'hostname' => 'fd35:4776:6804:2:2::2',
'password' => 'password',
'profile' => 'true'
}
}
};
}
I said "pretty much", because your sample data used the bare word 'true' for the slave->profile value - that's a syntax error in Perl - you can change it to a bare 1, or quote the value as "true" to make it work. In Perl, the digit zero, the string "0" or the empty/nul string "" all evaluate to "false" in a boolean context, anything else evaluates to "true". Take care if you choose to automate PHP to Perl translation, there may be other PHP-isms which could catch you out like that.
So much good information here and it helped me out quite a bit to come up with a working solution.
Here is the perl script I've got working:
#!/usr/bin/perl
use PHP::Include;
include_php_vars( 'config.local.php' );
my $test = \%config;
print $test->{'database'}->{'master'}->{'hostname'};
I also took the PHP array and changed it so that it no longer return array() but $config = array() and then return $config;
This did the trick for me. Thank you!
Related
Everyone, hello!
I'm currently trying to write to an .ini file from PHP, and I'm using Teoman Soygul's answer and code from here: How to read and write to an ini file with PHP
This works out great, although, when I save the data to it, it shows up strange in my .ini:
[Server] = ""
p_ip = "192.168.10.100"
p_port = 80
p_password = 1234
[Variable] = ""
string1_find = "Caution"
Most notably it also seems to see attempt to give the categories Server and Variable an empty value. Also, sometimes it saves the variable between consistency and sometimes not. How come there is no consistency here?
The code I'm using to find/post in PHP is this:
...
$a=array("[Server]"=>'',"p_ip"=>$_POST['pip'],"p_port"=>$_POST['pport'], "p_password"=>$_POST['pass'],
"[Variable]"=>'',"string1_find"=>$_POST['string1_find'],
...
If anyone could point me into the right direction, that would really be appreciated. Thank you!
You are not using right, you should be passing a multidimentional array instead:
$data = array(
'Server' => array(
'p_ip' => '192.168.10.100',
'p_port' => 80,
'p_password' => 1234,
),
'Variable' => array(
'string1_find' => 'Caution'
)
);
//now call the ini function from Soygul's answer
write_php_ini($data, 'file.ini');
Here is my output:
[Server]
p_ip = "192.168.10.100"
p_port = 80
p_password = 1234
[Variable]
string1_find = "Caution"
Notice that you need to create an extra array per new section and then you can start listing your custom definitions.
I am writing my first BASH script to automate configuration of my (Laravel) web projects.
I have some config files (app/config/local/database.php,app/config/app.php) with PHP arrays that I need to access and modify. For example ...
'providers' => array(
/** Append new service provider value, if it does not exist already */
'Illuminate\Foundation\Providers\ArtisanServiceProvider',
'Illuminate\Auth\AuthServiceProvider',
// ...
)
... or ...
'mysql' => array(
/** Replace value under key "database" to "test_db" */
'database' => 'homestead',
'username' => 'homestead',
)
So far I was using sed expressions like this:
$LV_DB_NAME="test_db"
$LV_DB_FILE="app/config/local/database.php"
gsed -i "s/'database' .*/'database' => '$LV_DB_NAME',/g" $LV_FILE_DB_CONFIG
This feels a little messy to me, especially in the case of example 1.
What would be awesome
Is there any way to get PHP arrays to BASH arrays and work with like you would in PHP?
Example 1
if (!in_array($new_provider, $providers)) {
$providers[] = $new_provider;
}
Example 2
$config['mysql']['database'] = $database_name
Update: What would also be awesome
If there is any other common way how to modify PHP arrays using terminal, I would be glad if you point me to it! I'm sure I'm not the only one who needs to modify PHP configuration arrays using terminal.
mTorres was actually right. If your machine has php cli installed (which is probable), You can easily jump into PHP from your bash scripts. There are multiple ways to do so, I finally settled with this:
print_s "Putting data to file \"$PATH\" ... "
export PATH=$PATH
export DATA_JSON=$DATA_JSON
/usr/bin/php << 'EOF'
<?php
$path = getenv("PATH");
$data = getenv("DATA_JSON");
$data = json_decode($data);
$config = (file_exists($path) && is_array($config_data = require($path))) ? $config_data : array();
foreach ($data as $k => $v) {
$config[$k] = $v;
}
file_put_contents($path, "<?php \n \n return ".var_export($config,true).";");
?>
EOF
}
There are some gotchas with passing associative arrays in BASH, check my other question: Pass BASH associative arrays to PHP script
I am creating a 3D Secure PHP Project. I am having a rather bizzare issue in that the "MD" code is going missing when re-submitting the Array of data
My code is as follows :
$paRes = $_REQUEST['PaRes'];
$md = $_REQUEST['MD'];
require "payment_method_3d.php";
x_load('cart','crypt','order','payment','tests');
/*
* For Debugging Purposes
* Only.
echo "The Value Of PaRes is : ";
echo $paRes;
*/
$soapClient = new SoapClient("https://www.secpay.com/java-bin/services/SECCardService?wsdl");
$params = array (
'mid' => '',
'vpn_pswd' => '',
'trans_id' => 'TRAN0095', // Transaction ID MUST match what was sent in payment_cc_new file
'md' => $md,
'paRes' => $paRes,
'options' => ''
);
It seems that the $_REQUEST['MD'] string seems to go missing AFTER the soap call. Although I am having difficulty print this out to the screen. The strange thing is the $paRes variable works without issue.
Any ideas why this would be the case?
Check your case. PHP array keys are case sensitive. From this little bit of code it looks as if the request variable may be 'md' instead of 'MD'.
Try $md = $_REQUEST['md'];
PHP array statements are case sensitive, so this should work:....
$md = $_REQUEST['md'];
Thanks for your responses guys.
What was happening was the include page was sitting in front of the request methods and causing issues loading the REQUEST methods to the page.
I want to build a php based site that (automate) some commands on my Ubuntu Server
first thing I did was going to the file (sudoers) and add the user www-data so I can execute php commands with root privileges!
# running the web apps with root power!!!
www-data ALL=(ALL) NOPASSWD: ALL
then my PHP code was
<?php
$command = "cat /etc/passwd | cut -d\":\" -f1";
echo 'running the command: <b>'.$command."</b><br />";
echo exec($command);
?>
it returns only one user (the last user) !!! how to make it return all users?
thank you
From the PHP manual on exec:
Return Values
The last line from the result of the
command. If you need to execute a
command and have all the data from the
command passed directly back without
any interference, use the passthru()
function.
To get the output of the executed command, be sure to set and use the
output parameter.
So you have to do something similar to this:
<?php
$output = array();
$command = "cat /etc/passwd | cut -d\":\" -f1";
echo 'running the command: <b>'.$command."</b><br />";
exec($command, &$output);
echo implode("<br />\n", $output);
?>
As #benjamin explains, no need to be root or sudo, no need for SUID.
Just pure PHP. I used the field names from posix_getpwnam.
function getUsers() {
$result = [];
/** #see http://php.net/manual/en/function.posix-getpwnam.php */
$keys = ['name', 'passwd', 'uid', 'gid', 'gecos', 'dir', 'shell'];
$handle = fopen('/etc/passwd', 'r');
if(!$handle){
throw new \RuntimeException("failed to open /etc/passwd for reading! ".print_r(error_get_last(),true));
}
while ( ($values = fgetcsv($handle, 1000, ':')) !== false ) {
$result[] = array_combine($keys, $values);
}
fclose($handle);
return $result;
}
It returns an array containing all users, formatted like this:
[
[
'name' => 'root',
'passwd' => 'x',
'uid' => '0',
'gid' => '0',
'gecos' => 'root',
'dir' => '/root',
'shell' => '/bin/bash',
],
[
'name' => 'daemon',
'passwd' => 'x',
'uid' => '1',
'gid' => '1',
'gecos' => 'daemon',
'dir' => '/usr/sbin',
'shell' => '/usr/sbin/nologin',
],
...
]
Like Matt S said, that's an incredibly bad idea to allow www-data root access on your server. The slightest compromise through your web applications could allow anyone full control of your system.
A better idea would be to make separate scripts for specific accessions then use SUID permissions. This means, a specific user (in this case, www-data) can make small changes to the system through the execution of scripts. Still not a good idea, though. You may be able to work around it with suPHP but security is still a major concern.
/etc/passwd is readable by anyone, so you should be able to execute your command without having any special rights (unless PHP prevents it?).
I find print_r in PHP extremely useful, but wonder if there is anything remotely equivalent in Perl?
Note #tchrist recommends Data::Dump over Data::Dumper. I wasn't aware of it, but from the looks of it, seems like it's both far easier to use and producing better looking and easier to interpret results.
Data::Dumper :
A snippet of the examples shown in the above link.
use Data::Dumper;
package Foo;
sub new {bless {'a' => 1, 'b' => sub { return "foo" }}, $_[0]};
package Fuz; # a weird REF-REF-SCALAR object
sub new {bless \($_ = \ 'fu\'z'), $_[0]};
package main;
$foo = Foo->new;
$fuz = Fuz->new;
$boo = [ 1, [], "abcd", \*foo,
{1 => 'a', 023 => 'b', 0x45 => 'c'},
\\"p\q\'r", $foo, $fuz];
########
# simple usage
########
$bar = eval(Dumper($boo));
print($#) if $#;
print Dumper($boo), Dumper($bar); # pretty print (no array indices)
$Data::Dumper::Terse = 1; # don't output names where feasible
$Data::Dumper::Indent = 0; # turn off all pretty print
print Dumper($boo), "\n";
$Data::Dumper::Indent = 1; # mild pretty print
print Dumper($boo);
$Data::Dumper::Indent = 3; # pretty print with array indices
print Dumper($boo);
$Data::Dumper::Useqq = 1; # print strings in double quotes
print Dumper($boo);
As usually with Perl, you might prefer alternative solutions to the venerable Data::Dumper:
Data::Dump::Streamer has a terser output than Data::Dumper, and can also serialize some data better than Data::Dumper,
YAML (or Yaml::Syck, or an other YAML module) generate the data in YAML, which is quite legible.
And of course with the debugger, you can display any variable with the 'x' command. I particularly like the form 'x 2 $complex_structure' where 2 (or any number) tells the debugger to display only 2 levels of nested data.
An alternative to Data::Dumper that does not produce valid Perl code but instead a more skimmable format (same as the x command of the Perl debugger) is Dumpvalue. It also consumes a lot less memory.
As well, there is Data::Dump::Streamer, which is more accurate in various edge and corner cases than Data::Dumper is.
I use Data::Dump, it's output is a bit cleaner than Data::Dumper's (no $VAR1), it provides quick shortcuts and it also tries to DTRT, i.e. it will print to STDERR when called in void context and return the dump string when not.
I went looking for the same thing and found this lovely little Perl function, explicitly meant to generate results like print_r().
The author of the script was asking your exact question in a forum here.
print objectToString($json_data);
Gives this output:
HASH {
time => 1233173875
error => 0
node => HASH {
vid => 1011
moderate => 0
field_datestring => ARRAY {
HASH {
value => August 30, 1979
}
}
field_tagged_persons => ARRAY {
HASH {
nid => undef
}
}
...and so on...