Method's optional parameter, when passed as string, becomes an array - php

Tl;dr: at the bottom
Setup:
I have a cron job that calls a PHP script to handle some Backend tasks. For simplicity, the cron job redirects all output to a log file. Because this matters to my actual question found below, here's the sanitized format of the cron job:
15 4 * * * php /usr/local/bin/myScript.php >> /home/$USER/scriptLogs/myScript.log 2>&1
I'm new-ish to OOP and I'm being tasked with learning it as I go, and for myScript.php, I'm doing some data imports that require querying the DB to validate the data prior to its import and I'm supposed to log every transaction. We recently moved from 5.6 to 7.2 and part of task at hand is to use 7.2's new features as we refactor.
The refactor itself is to take all of the duplicated code and move it to classes to obey the DRY principle.
Previously, it looked something like this:
<?php namespace CronJobs
use PDO;
use Exception;
class JobAt415 {
private function getDBconnection()
{
// connects to a DB through environment variable set in a config file
return $db;
}
public function query1($parameter1, $parameter2, $inclusionParameter)
{
$sql = "SELECT " . $parameter1 . ", ". $parameter2 . " FROM `foo`.`bar` WHERE " . $inclusionParmeter " IS NOT NULL;";
try
{
$db = $this->getDBconnection();
echo '[' . strftime("%c") . '] Running query 1' . PHP_EOL;
$resultDictionary = $db->query($sql)->fetchall(PDO::FETCH_KEY_PAIR)
return $resultDictionary;
}
catch (Exception $e)
{
echo '[' . strftime("%c") . '] ERRORS ' . PHP_EOL;
echo $e->getMessage();
return null;
}
}
public function query2($parameter1, $parameter3)
{
$sql = "SELECT " . $parameter1 . " FROM `foo`.`bar` WHERE " . $parameter3 " > 0;";
try
{
$db = $this->getDBconnection();
echo '[' . strftime("%c") . '] Running query 1' . PHP_EOL;
$resultDictionary = $db->query($sql)->fetchall()
return $resultArray;
}
catch (Exception $e)
{
echo '[' . strftime("%c") . '] ERRORS ' . PHP_EOL;
echo $e->getMessage();
return null;
}
}
}
Post-Refactor:
<?php namespace CronJobs
use PDO;
use Exception;
Class DbConnectionFactory {
protected $dbConnection;
public function __construct()
{
$this->dbConnection = $this->createConnection();
}
public function runQuery($sql, ...$queryDescriptor)
{
try
{
$descriptor = $queryDescriptor ? (string) $queryDescriptor : $sql;
echo '[' . strftime("%c") . '] Running query ' . "$descriptor" . PHP_EOL;
$resultPending = $this->dbConnection->query($sql);
echo '[' . strftime("%c") . '] Query successful.' . PHP_EOL;
return $resultPending;
}
catch (Exception $e)
{
echo '[' . strftime("%c") . '] ERRORS ' . PHP_EOL;
echo $e->getMessage();
return null;
}
}
public function runQueryFetchDictionary($sql, ...$queryDescriptor)
{
$description = (string) $queryDescriptor;
$fetchAll = $this->runQuery($sql, $description)->fetchall(PDO::FETCH_KEY_PAIR);
return $fetchAll;
}
// In the JobAt415 class
private function runQuery1()
{
$sql = 'SELECT `parameter1`, `parameter2` FROM `foo`.`bar` WHERE `baz` IS NOT NULL;';
$description = 'p1, p2 :: baz != NULL';
$p1Dictionary = $this->db->runQueryFetchDictionary($sql, $descripton); // $this->db is an instantiation of the DbConnectionFactory class
So, now I just pass the SQL query as a parameter and a description of what is being queried to be echoed to the log, and I don't have 19 try/catch blocks in the code or a bunch of duplicated code that I've removed from this example.
Unfortunately, as I'm stepping through the code with XDebug, the optional parameter $queryDescriptor is being converted from a string to an array. I've tried multiple ways of passing it, casting it, and/or defining it and get the same result: $queryDescriptor is an array. At one point, casting it to a string returned the value of "Array".
When I checked the PHP website, I found this:
Note:
The behaviour of an automatic conversion to array is currently undefined.
Emphasis mine.
I don't want any conversion. So, how do I prevent this? Does anyone see what I'm missing? Why doesn't the $sql string get converted to an array but the $queryDescriptor always get converted?
Tl;dr:
Why is my string now an array?

Because by adding a parameter like ...$queryDescriptor you tell PHP that there can be endless parameters. This is because of the ... in front of the variable name. That's why PHP changes the type to an array.
Otherwise how could you handle the number of possibly thousand of parameters?
https://secure.php.net/manual/en/functions.arguments.php#functions.variable-arg-list
//You tell PHP that there can be a variable number of parameters by adding '...' in front of the variable name
function foo(...$bar) {
//You get an array holding all the passed parameters
foreach($bar as $arg) {
//By stepping through it you get all the parameters
echo $arg;
}
}
Or of course you can get the parameters by their indexes.
$bar[0]; //returns the first passed parameter

Related

Why is Tinybutstrong Mergeblock giving me the error: 'item before ... is neither an object nor an array. Its type is NULL..'

I am trying to use Tinybutstrong to merge a template with an sql query result. Below is the PHP code being run, the custom database reader plugin I am attempting to use, the template file and the error message.... The code is being run from within a wordpress site, hence the use of the wpdb object and the custom database reader plugin.
The code being run:
include_once('wp-content/themes/mine/tbs_plugins/tbsdb_wpdb.php');
$link = new wpdb($username, $password, $dbname, $servername);
$sql_query = 'select year from mytable limit 10';
$TBS = new clsTinyButStrong;
$TBS->LoadTemplate('wp-content/themes/mine/templates/mytemplate.htm');
$TBS->MergeBlock('blk1', $link, $sql_query);
$TBS->Show();
Custom database reader plugin tbsdb_wpdb.php:
<?php
function tbsdb_wpdb_open(&$source,&$query) {
$source->get_results($source->prepare($query),ARRAY_A);
return $source;
}
function tbsdb_wpdb_fetch(&$Rs,$num) {
if ($num<=$Rs->num_rows) {
return $Rs->get_row(null,ARRAY_A,$num-1) ;
}
else {
return False ;
}
}
function tbsdb_wpdb_close(&$source) {
// not needed
}
?>
Part of Template mytemplate.htm:
.
.
.
[blk1;block=begin]
<table><tr><td align="center">[blk1.year]</td></tr></table>
[blk1;block=end]
.
.
.
Error message:
TinyButStrong Error in field [blk1.year...]: item before 'year' is neither an object nor an array. Its type is NULL. This message can be cancelled using parameter 'noerr'.
I have listed out the results from the query in a for loop as follows:
$rows = $link->get_results($sql_query);
echo "<table>";
foreach ($rows as $obj) :
echo "<tr><td>" . $obj->Year . "</td></tr>";
endforeach;
echo "</table>";
this gives me a correct result but when using TBS class/Loadtemplate/Mergeblock/Show... no joy, I get the error message... Any ideas would be appreciated.
Solved it myself by modifying the tbsdb_wpdb.php to:
function tbsdb_wpdb_open(&$source,&$query) {
global $link;
global $sql_query;
return $link->get_results($sql_query,ARRAY_A);
}
function tbsdb_wpdb_fetch(&$Rs,$num) {
global $link;
global $sql_query;
if ($num<=$link->num_rows) {
return $link->get_row($sql_query,ARRAY_A,$num-1) ;
}
else {
return False ;
}
}
So essentially my global declarations were missing (or rather, in the wrong place).

In Yii2 make db query(include 'join' expression) to oracle, cannot fetch any result

Here's the code in controller
public function actionIndex()
{
$result = new RestResult();
try {
$query = TaskMgr::find()
->join('INNER JOIN', EqpInfo::tableName(), EqpInfo::tableName() . '.EQP_COD =' . TaskMgr::tableName() . '.EQP_COD')
->select([TaskMgr::tableName() . '.EQP_COD', TaskMgr::tableName() . '.TASK_STA', TaskMgr::tableName() . '.TASK_DATE', TaskMgr::tableName() . '.BUSINESS_NATURE', EqpInfo::tableName() . '.EQP_SORT_NAME', EqpInfo::tableName() . '.EQP_VART_NAME', EqpInfo::tableName() . '.USE_UNT_NAME', EqpInfo::tableName() . '.USE_UNT_ADDR', EqpInfo::tableName() . '.INST_AREA_NAME', EqpInfo::tableName() . '.MAKE_UNT_NAME']);
$result->content = $query->asArray(true)->all();
} catch (Exception $e) {
$result->error = $e->getMessage();
$result->content = [];
$result->message = ["error" => Yii::t('rest', "Server error.")];
} finally {
return $result;
}
}
and I got empty array.as pic 1, I thought it might be the problem of QueryBuilder, so I simply excute the sql script in index action:
public function actionIndex()
{
$y = Yii::$app->get('db2')->createCommand('SELECT * FROM "TB_TASK_MGR" INNER JOIN "V_EQPINFO" ON "V_EQPINFO".EQP_COD ="TB_TASK_MGR".EQP_COD')
->queryAll();
return var_dump($y);
}
and I got a empty array again.
but when I ran the sql script in Navicat, I got some rows.as pic 2, that means the sql result is not empty. I thought it might some problems with my DBConnection, so I tried change the action as :
public function actionIndex()
{
$y = Yii::$app->get('db2')->createCommand('SELECT * FROM "TB_TASK_MGR"')
->queryAll();
return var_dump($y);
}
and I got some rows, that means there's no problem in DBConnection, the only difference is the 'join' expression.
I use xampp to deploy site, php version is 5.6.23. oracle client installed and remote oracle version is 11g. php.ini config oci as
extension=php_pdo_oci.dll
Please help me find out why is this, is it because some bugs in php_pdo_oci.dll? and how to excute sql with 'join' in yii2 to oracle.

OOP PHP not working, the web page returns nothing>

I'm trying my first OOP PHP, but I seem to have hit a wall. As far as i can see there is nothing wrong with the code, and i get no error message. Thanks already!
<?PHP
class President {
public $lastname;
public $dob;
public $dod;
public $firstlady;
public $wikipage;
public $display;
public $party;
public $inoffice;
public function __construct ($name, $dob, $dod, $girl, $wiki, $pic, $party, $terms){
$this->lastname = $name;
$this->dob = $dob;
$this->dod = $dod;
$this->firstlady = $girl;
$this->wikipage = $wiki;
$this->display = $pic;
$this->party = $party;
$this->inoffice = $terms;
}
public function Write(){
return "President". $name . "was in office for" . $terms . "." . "He was born on" . $dob . "and" . $dod . "." . "He represented the" . $party . "and lived in the Whitehouse with his wife" .
$girl . "<br/>" . "<img src'" . $pic . "' />" . "<br />" . "<a href'" . $wiki . "'> Read more on Wikipedia</a>";
}
}
$obama = new President();
$obama->Write('Obama', 'June First 1992', 'is still alive', 'Michelle Obama', 'http://www.google.com', 'www.google.com/', 'Democrat', 'Two Terms');
echo $obama;
?>
first, turn on error reporting... then..
2 problems that I can see.
Firstly, youre constructor is expecting lots of parameters, which you are not passing when you instantiate the object, but when you call the Write method, which isnt expecting anything.
Then, you dont echo $obama->Write(..) when it returns a string.
solution:
$obama = new President('Obama', 'June First 1992', 'is still alive', 'Michelle Obama', 'http://www.google.com', 'www.google.com/', 'Democrat', 'Two Terms');
echo $obama->Write();
should work assuming that your parameters are right in your construct.
edit
as stated in comments below, your write method wont be able to access any of the class vars as its only looking at the local scope. You need to change your variable calls like this:
return "President". $name
to
return "President". $this->name

call to member function on non object

Can someone help a beginner?
It says that Call to a member function set() on a non-object; I have been tinkering with this for 3 days and am unable to figure out what is causing this error.
here is the code
function torrent_download($item_details, $output_type = 'show_torrent')
{
$template->set('session', $session);
$torrent = $show->torrent_download($session->value('user_id'));
{
if ($session->value('is_seller'))
{
$show_tips = $db->count_rows('users', "WHERE user_id='" . $session->value('user_id') . "' AND notif_a=0");
if ($show_tips)
{
$msg_member_tips = '<p class="errormessage">' . MSG_MEMBER_TIPS_A . '<br>' . MSG_MEMBER_TIPS_B . '</p>';
$db->query("UPDATE " . DB_PREFIX . "users SET notif_a=1 WHERE user_id='" . $session->value('user_id') . "'");
}
$template->set('msg_member_tips', $msg_member_tips);
}
if (isset($_REQUEST['form_download_proceed']))
{
$download_result = download_redirect($_REQUEST['winner_id'], $session->value('user_id'));
if ($download_result['redirect'])
{
header('Location: ' . $download_result['url']);
}
$template->set('msg_changes_saved', '<p align="center">' . $download_result['display'] . '</p>');
}
}
}
I'm assuming you're getting this error inside the torrent_download function?
If so, that's because $template is undefined. Perhaps you defined it in the global script, but functions don't inherit that.
You can prevent this issue by adding: global $template (and any other variable from the outside you need) to the start of your function.
This generally means your calling something on a variable that's null. What line number is the error returning??
You should add checks after your assignments to ensure the follow are not null:
$download_result, $template, $torrent
From what I can tell, $template is never defined.

php script executes but no output

I have gone through all the similar questions and nothing fits the bill.
I am running a big script, which ran on chron on an old server but failed on the new so I am working on and testing in browser.
I have two functions, one pulls properties from the database, and then runs them through another which converts the price into 4 currencies, and if the value is different updates the row. The functions are as follows:
<?php
function convert_price($fore_currency, $aft_currency, $amount)
{
echo "going into convert<br/>";
$url = "http://www.currency.me.uk/remote/ER-ERC-AJAX.php?ConvertFrom=" . $fore_currency .
"&ConvertTo=" . $aft_currency . "&amount=" . $amount;
if (!is_int((int)file_get_contents($url))) {
//echo "Failed on convert<br/>";
return false;
} else {
//echo "Conversion done";
return (int)file_get_contents($url);
}
}
function run_conversion($refno = '', $output = false)
{
global $wpdb;
$currencies = array("GBP", "EUR", "TRY", "USD");
$q = "SELECT * FROM Properties ";
$q .= (!empty($refno)) ? " WHERE Refno='" . $refno . "'" : "";
$rows = $wpdb->get_results($wpdb->prepare($q), ARRAY_A);
$currencies = array("USD", "GBP", "TRY", "EUR");
//$wpdb->show_errors();
echo "in Run Conversion " . "<br/>";
foreach ($rows as $row) {
echo "In ROw <br/>";
foreach ($currencies as $currency) {
if ($currency != $row['Currency'] && $row['Price'] != 0) {
$currfield = $currency . "_Price";
$newprice = convert_price($row['Currency'], $currency, $row['Price']);
echo "Old Price Was " . $row['Price'] . " New Price Is " . $newprice . "<br/>";
if ($newprice) {
if ($row[$currfield] != $newprice) {
$newq = "UPDATE Properties SET DateUpdated = '" . date("Y-m-d h:i:s") . "', " .
$currfield . "=" . $newprice . " WHERE Refno='" . $row['Refno'] . "'";
$newr = $wpdb->query($newq);
if ($output) {
echo $newq . " executed <br/>";
} else {
echo "query failed " . $wpdb->print_error();
}
} else {
if ($output) {
echo "No need to update " . $row['Refno'] . " with " . $newprice . "<br/>";
}
}
} else {
echo "Currency conversion failed";
}
}
}
}
}
?>
I then run the process from a seperate file for the sake of chron like so:
require($_SERVER['DOCUMENT_ROOT'] . "/functions.php"); // page containing functions
run_conversion('',true);
If I limit the mysql query to 100 properties it runs fine and I get all the output in a nice stream. But when I try to run it in full the script completes (I can see from rows updated in db) but no output. I have tried upping the memory allowance but no joy. Any ideas gratefully received. Also, I get a 500 error when changing from Apache Module to CGI. Any ideas on that also well received as ideally I would like to turn site onto fastCGI.
You're running out of memory most likely. Probably in your DB layer. Your best bet is to unset your iterative values at the end of each loop:
foreach ( $rows as $row ) {
...
foreach ( $currencies as $currency ) {
...
unset( $currency, $newr, $currfield, $newprice );
}
unset( $row );
}
This will definitely help.
As another answer suggests you may be running out of memory, but if you make an HTTP call everytime the loop is running, and you have 100+ currencies, this may also cause the problem. Try limit it to 5 currencies for example.
If this is indeed the problem I would split the process, so a chunk of currencies have their respective value updated per cron job, instead of everything.

Categories