CSV import problem since converting to PHP 8.1 - csv

I have the following Wordpress function that worked in PHP 7. Since converting to 8.1, it's not working.
function dropdown_handler() {
$output = drop_function();
//send back text to replace shortcode in post
return $output;
}
function drop_function() {
//get the csv file with amounts
if ($file_handle = fopen("wp-content/plugins/drop/amounts.csv", "r")) {
while (!feof($file_handle) ) {
$lines[] = fgetcsv($file_handle, 1024);
}
fclose($file_handle);
$lines = str_replace ("£","£",$lines);
}
else {
echo "Sorry, something went wrong";
}
In my error log I'm seeing "PHP Warning: Array to string conversion in" relating to the $lines = str_replace line but I think there's something wrong with the fopen statement.
Basically, the word Array is being stored in the $lines variable rather than the contents of the CSV file.
Any ideas please?

Your code was always broken, it's just broken in a slightly more obvious way than it used to be...
$lines[] = fgetcsv($file_handle, 1024);
fgetcsv, unless it fails, returns an array; you then add this array as a new item to another array, $lines. The result is an array of arrays, like this:
$lines = [
['line 1 first item', 'line 1 second item'],
['line 2 first item', 'line 2 second item'],
];
Later, you pass this whole array to str_replace; but str_replace only knows how to deal with a single dimension of array.
So this works:
$singleLine = ['line 1 first item', 'line 1 second item'];
var_dump(str_replace('item', 'ITEM', $singleLine));
But this doesn't:
var_dump(str_replace('item', 'ITEM', $lines));
Running that example on multiple versions of PHP reveals that under PHP 7.x, str_replace reacted by simply leaving the inner arrays untouched - in other words, it did nothing.
In PHP 8, it instead tries to turn each inner array into a string, issuing the warning and producing the word "Array" (which will then have any substitutions applied to it).
The fix for both PHP versions is to run the str_replace on each of the inner arrays, most simply by using array_map:
var_dump(
array_map(
fn($innerArray) => str_replace('item', 'ITEM', $innerArray),
$lines
)
);
Alternatively, you can just delete the str_replace line completely, since you were apparently happy enough when it wasn't actually doing anything.

Related

Strange behaviour from MySQL in Powershell

I'm new to PowerShell and have a specific question about working with MySQL in PowerShell.
I got this function:
Function run-mySQLInsertQuery{
param(
$connection,
[string[]]$insertQuery
)
foreach ($command in $insertQuery){
$MySQLCommand = $connection.CreateCommand()
$MySQLCommand.CommandText = $command
$rowsInserted = $MySQLCommand.ExecuteNonQuery()
if ($rowsInserted) {
return $rowsInserted
} else {
return $false
}
}
}
With this version of the function i get the following Error:
Cause:
"The CommandText property has not been properly initialized."
Errorline:
$rowsInserted = $MySQLCommand.ExecuteNonQuery()
I searched for a solution and edited my function a bit to the following (for testing purpose):
Function run-mySQLInsertQuery{
param(
$connection,
[string[]]$insertQuery
)
$abcd = $insertQuery[1]
foreach ($command in $insertQuery){
$MySQLCommand = $connection.CreateCommand()
$MySQLCommand.CommandText = $abcd
$rowsInserted = $MySQLCommand.ExecuteNonQuery()
}
}
With this code, the function executes the query without a problem. My question now is, why? i cant really see a difference, because in $command should be the exact same query like it is in $abcd. Or am I getting something wrong?
EDIT:
As its asked it the comments, here is how i call the function:
[String[]]$statements = ""
foreach($key in $arrayStatus.Keys){
$item = $arrayStatus[$key]
$insertStatus = "INSERT INTO tx_tphbusinessofferings_domain_model_status (status_id, status) VALUES ('$key', '$item')"
$statements += $insertStatus
}
$Rows = run-mySQLInsertQuery -connection $mySQLconnection -insertQuery $statements
The problem is that you are initializing your array (the one you are passing in) with an empty string:
[String[]]$statements = ""
And then adding elements to it... so your first iteration of the passed array is an empty string, which won't work (it'll set the command text as empty, that's the error you are getting). It works on the second code because you are grabbing the second object of the array (which is your insert statement).
Initialize your array to empty and it should work:
[String[]]$statements = #()
Apart from that, your first script always returns on the first iteration, so it'll only work once (not for every insert you pass). Not sure what do you want to return if you are passing in more than one query, but that's up to your design decisions

'Not an ARRAY reference' error thrown

I'm writing a Perl script that is meant to deal with an API which returns metrics about a set of URLs that I pull from MySQL then post these metrics back into a different table. Currently this piece of code:
my $content = $response->content;
my $jsontext = json_to_perl($content);
my $adsql = 'INSERT INTO moz (url_id,page_authority,domain_authority,links,MozRank_URL,MozRank_Subdomain,external_equity_links) VALUES (?,?,?,?,?,?,?)';
my $adrs = $db->prepare( $adsql );
my $adsql2 = 'UPDATE url
SET moz_crawl_date = NOW()
where url_id = ?;';
my $adrs2 = $db->prepare( $adsql2 );
my $currentUrlId = 0;
foreach my $row (#$jsontext){
$adrs->execute($url_ids[$currentUrlId], $row->{'fmrp'}, $row->{'upa'}, $row->{'pda'}, $row->{'uid'}, $row->{'umrp'}, $row->{'ueid'});# || &die_clean("Couldn't execute\n$adsql\n".$db->errstr."\n" );
$adrs2->execute($url_ids[$currentUrlId]);
$currentUrlId++;
}
is throwing this error:
Not an ARRAY reference at ./moz2.pl line 124.
this is line 124:
foreach my $row (#$jsontext){
this whole chunk of code is in a while loop. I am actually able to iterate a couple times and fill my MySQL table before the script fails (technically the program works, but I don't want to just leave an error in it).
Anybody have any suggestions?
Perl gave you the correct answer
Not an ARRAY reference: #$jsontext
You are dereferencing $jsontext, which is the result of json_to_perl(string), to an array.
But json_to_perl() didn't return an arrayref.
json_to_perl seems to be from this API: http://search.cpan.org/~bkb/JSON-Parse-0.31/lib/JSON/Parse.pod#json_to_perl
which returns according to the doc either an arrayref or a hashref.
Apparently it did return a hashref in your case, so you have to add the logic to deal with the HASH case. Which seems to be a single row.
if (ref $jsontext eq 'HASH') {
# seems to be a single row
$adrs->execute($url_ids[$currentUrlId], $jsontext->{'fmrp'}, $jsontext->'upa'}, $jsontext->'pda'}, $jsontext->'uid'}, $jsontext->'umrp'}, $jsontext->'ueid'});# || &die_clean("Couldn't execute\n$adsql\n".$db->errstr."\n" );
$adrs2->execute($url_ids[$currentUrlId]);
$currentUrlId++;
} elsif (ref $jsontext eq 'ARRAY') {
foreach my $row (#$jsontext){
$adrs->execute($url_ids[$currentUrlId], $row->{'fmrp'}, $row->{'upa'}, $row->{'pda'}, $row->{'uid'}, $row->{'umrp'}, $row->{'ueid'});# || &die_clean("Couldn't execute\n$adsql\n".$db->errstr."\n" );
$adrs2->execute($url_ids[$currentUrlId]);
$currentUrlId++;
}
}

getting JSON from DBI:Pg selectall_arrayref

I have an array
my #cols = ("accountid", "balance");
and a dataset
my $rowsref=$dbh->selectall_arrayref($_[0]);
foreach my $row (#$rowsref) {
print join(", ", map {defined $_ ? $_ : "(null)"} #$row), "\n";
}
which prints "1, 150".
I would like to get a JSON output like [{"accountid": 1, "balance": 150},{..}].
I have the JSON module loaded, but unsure how to merge #cols with each $row.
edit: added explanation of column name transaction in 2nd example
edit: Fixed for your requirement of a single JSON encoding of the whole resultset.
edit: forgot keys in cols mapping code in 2nd example.
edit: typo'd hashref everywhere to arrayref. :-
Firstly, use selectall_hashref instead, so that it already contains the key names.
Then use one of the JSON encoding modules to encode each row.
(making the same assumptions as your code....)
Using the list-of-hashrefs from selectall_hashref() as-is:
use JSON::XS;
use 5.10.0;
my $rowsref = $dbh->selectall_hashref($_[0]);
print JSON::XS::encode_json($rowsref),"\n";
Performing translation on colnames from selectall_hashref():
If the column names from the database aren't the same as your column names, then you'll need a mapping:
use JSON::XS;
use 5.10.0;
my $trans = { account => 'accountid', amount => 'balance' };
my $rowsref = $dbh->selectall_hashref($_[0]);
my $output = [];
for my $row (#$rowsref) {
push #$output, {
map {
my $colname = exists($trans->{$_}) ? $trans->{$_} : $_;
my $value = $row->{$_};
$colname => $value;
} keys %$row
});
}
print JSON::XS::encode_json($output),"\n";
For each $row above of the resultset, keys %$row gives back the column names in the row as returned from the database.
The map operation takes each of those column names and producues 2 scalar values; (1) $colname is either the original database column name or (if it's found in the $trans hashref) a 'translation' of the column name; (2) $value is the value returned by the database for this column name in this particular $row. $colname => $value returns both the $colname and $value from the map as a 'flattened' pair of scalar values. That means that the map operation returns a list of scalar values twice as long as the original list of column names returned by keys %$row.
Finally, the push #$output, { ... } creates an anonymous hash reference from that list of scalar values in key,value,key,value,... order and adds it to the end of the $output array reference.
Blind translation from selectall_arrayref()
If (for some reason) you have a pathological aversion to querying hashrefs from your database, I guess you could do:
use 5.10.0;
my #cols = ("accountid", "balance");
my $rowsref = $dbh->selectall_arrayref($_[0]);
my $output = [];
for my $row (#$rowsref) {
my %row = map { $cols[$_] => $row->[$_] } (0 .. $#cols);
push #$output, \%row;
}
print JSON::XS::encode_json($output),"\n";
That assumes there are only two columns coming back from the query, though.
"Defined or" operator:
By the way... assuming a late enough perl, you can replace this sort of thing:
defined $_ ? $_ : "(null)"
with this:
$_ // "(null)"
Your code editor (e.g: Vim) might not syntax highlight it correctly if it's not up to date with perl. (e.g: it might treat it as an m// construct).
Note that PostgreSQL can also generate JSON. If it is an option for you then Perl JSON module is redundant.

Include spaces while reading CSV file

I have an old server running PHP 5.2. I want to migrate it to a server that uses PHP 5.4
One of my scripts is reading a csv file and the results are different.
I have a line like this in the CSV:
Id, Date, Description
On my old server this returns an array:
array('Id', 'Date', 'Description');
On my new server I get this:
array('Id', ' Date', ' Description');
Which is causing bugs. Now technically I could go in every row, and trim the spaces, but I have files with about 500,000 lines, and adding a simple process might slow down the code.
I was wondering, is there a way to make the new server act as the old one ? (without downgrading PHP obviously)
EDIT: Here is the script itself:
if (($handle = fopen($_FILES["csvfile"]["tmp_name"], 'r')) !== FALSE) {
while (($row = fgetcsv($handle, 10000, ',')) !== FALSE) {
if (!$header)
$header = $row;
else
$filec[] = array_combine($header, $row);
}
fclose($handle);
}
Use one of the following:
Encode the csv file as UTF-8
Preprocess the csv file

Problems parsing Reddit's JSON

I'm working on a perl script that parses reddit's JSON using the JSON module.
However I do have the problem of being very new to both perl and json.
I managed to parse the front page and subreddits successfully, but the comments have a different structure and I can't figure out how to access the data I need.
Here's the code that successfully finds the "data" hash for the front page and subreddits:
foreach my $children(#{$json_text->{"data"}->{"children"}}) #For values of children.
{
my $data = $children->{"data"}; #accessing each data hash.
my %phsh = (); #my hash to collect and print.
$phsh{author} = $data->{"author"};#Here I get the "author" value from "data"
*Etc....
This successfully gets what I need from http://www.reddit.com/.json
But when I go to the json of a comment, this one for example, it has a different format and I can't figure out how to parse it. If I try the same thing as before my parser crashes, saying it is not a HASH reference.
So my question is: How do access the "children" in the second JSON? I need to get both the data for the Post and the data for the comments. Can anybody help?
Thanks in advance!
(I know it may be obvious, but I'm running on very little sleep XD)
You need to either look at the JSON data or dump the decoded data to see what form it takes. The comment data, for example is an array at the top level.
Here is some code that prints the body field of all top-level comments. Note that a comment may have an array of replies in its replies field, and each reply may also have replies in turn.
Depending on what you want to do you may need to check whether a reference is to an array or a hash by checking the value returned by the ref operator.
use strict;
use warnings;
binmode STDOUT, ':utf8';
use JSON;
use LWP;
use Data::Dump;
my $ua = LWP::UserAgent->new;
my $resp = $ua->get('http://www.reddit.com/r/funny/comments/wx3n5/caption_win.json');
die $resp->status_line unless $resp->is_success;
my $json = $resp->decoded_content;
my $data = decode_json($json);
die "Error: $data->{error}" if ref $data eq 'HASH' and exists $data->{error};
dd $data->[1]{data}{children}[0];
print "\n\n";
my $children = $data->[1]{data}{children};
print scalar #$children, " comments:\n\n";
for my $child (#$children) {
print $child->{data}{body}, "\n";
}