Can not redeclare function doing while loop - php

I'm new to functions and I'm probably not doing it as well as I could so could use some help with this. I'm pulling stock information from a web sites API. All the information being pulled is done with several .php pages and I have one file that includes all the necessary pages. So I have something like this:
pullBalanceSheet.php for all the balance sheet data
pullIncomeStatement.php for all the income sheet data
etc, there are 10 pages. All pages can run independently of each other and was created like this to make easy to test each API call.
I have a master page (pullData.php) that contains the necessary includes for each page so it effectively runs all the pages as a single page. I hope this makes sense.
Some of my pages contain a function and this is where I'm probably not doing it the best way, but the pages work to pull the correct data. These pages pull quarterly financial information for the last 5 years. The issue is that every company states quarterly financial dates differently. So let's say it's January 2019 and some company quarterly will state Quarter 2, 2019 even though it really isn't Quarter 2 2019 until March 2019. So my code basically needs to capture possible future dates, so my code looks like this:
$fiscal_year = date("Y");
$fiscalPeriod = 'Q1';
pulldataBS($fiscal_year,$fiscalPeriod, $username,$password,$apiKey);
$fiscalPeriod = 'Q2';
pulldataBS($fiscal_year,$fiscalPeriod, $username,$password,$apiKey);
$fiscalPeriod = 'Q3';
pulldataBS($fiscal_year,$fiscalPeriod, $username,$password,$apiKey);
$fiscalPeriod = 'Q4';
pulldataBS($fiscal_year,$fiscalPeriod, $username,$password,$apiKey);
//year 2
$fiscal_year = $fiscal_year-1;
$fiscalPeriod = 'Q1';
pulldataBS($fiscal_year,$fiscalPeriod, $username,$password,$apiKey);
$fiscalPeriod = 'Q2';
pulldataBS($fiscal_year,$fiscalPeriod, $username,$password,$apiKey);
$fiscalPeriod = 'Q3';
pulldataBS($fiscal_year,$fiscalPeriod, $username,$password,$apiKey);
$fiscalPeriod = 'Q4';
pulldataBS($fiscal_year,$fiscalPeriod, $username,$password,$apiKey);
..etc for 5 years. The API looks at the $fiscal_year and the $fiscal_period and returns data if it's available.
The function is this:
function pullDataBS($fiscal_year,$fiscalPeriod, $username,$password,$apiKey) {
// function code here
}
All my code works when pulling one stock. I'm now working on pulling multiple stocks by using a while loop on my pullData.php master page and this is where I'm having the issue. Since the code is repeating itself for each new stock, I'm getting a fatal error cannot redeclare function. I understand why this is happening since the loop is being treated as a single page and when it hits the second stock, it errors. I'm trying to figure out how to get around this. I have used
if(!function_exists('pullDataBS')) {
function pullDataBS($fiscal_year,$fiscalPeriod, $username,$password,$apiKey) {
//function code here
}
}
And I get a undefined function pulldataBS error when it hits the function call at the very first time (3rd line of code where it tries the first pulldataBS in this example). Here is my pulldata.php master page:
while($research = mysqli_fetch_array($queryResearch)) {
$_SESSION['ticker'] = $research['symbol'];
include('pullBalanceSheet.php');
include('pullIncomeStatement.php');
// etc
}
I'm assuming the !function_exists is not correct or I have some other critical issue with the way I coded my function. I hope this all makes sense. Let me know if you need any other code. Thanks in advance!!

First of all, you should not include pullBalanceSheet.php and pullIncomeStatement.php files in each iteration of while loop. Take below two lines outside of while loop.
include('pullBalanceSheet.php');
include('pullIncomeStatement.php');
Now comes to the problem, you must not declare your function(s) over and over again, just declare it once and call it (with appropriate arguments) multiple times as per your need. Having said that, you can use defined() along with define() functions to solve your function redelaration problem.
if(!defined('PULL_DATA_BS')){
define('PULL_DATA_BS', 'pullDataBS');
function pullDataBS($fiscal_year,$fiscalPeriod, $username,$password,$apiKey) {
//function code here
}
}
This way this function won't get declared more than once. Ideally, you are supposed to encapsulate the entire code of a file with below block.
xyz.php
<?php
if(!defined('XYZ_PHP')){
define('XYZ_PHP', 'xyz.php');
// file code
}
?>
This way, you will prevent yourself(and somebody else) from including this file code more than once.

Related

Variables being changed by TeamSpeak API for PHP

I'm developing a tool for a website and I came up with an odd problem, or better, an odd situation.
I'm using the code bellow to retrieve data from the TeamSpeak server. I use this info to build a profile on a user.
$ts3 = TeamSpeak3::factory("serverquery://dadada:dadada#dadada:1234/");
// Get the clients list
$a=$ts3->clientList();
// Get the groups list
$b=$ts3->ServerGroupList();
// Get the channels list
$c=$ts3->channelList();
Now, the odd situation is that the output of this code block:
// Get the clients list
$a=$ts3->clientList();
// Get the groups list
$b=$ts3->ServerGroupList();
// Get the channels list
$c=$ts3->channelList();
echo "<pre>";print_r($a);die();
(Notice the print_r)
Is totally different from the output of this code block:
// Get the clients list
$a=$ts3->clientList();
// Get the groups list
#$b=$ts3->ServerGroupList();
// Get the channels list
#$c=$ts3->channelList();
echo "<pre>";print_r($a);die();
What I mean is, the functions I call after clientList() (which output I store in the variable $a) are changing that variable's contents. This is, they're kind of appending their output to the variable.
I've never learned PHP professionally, I'm just trying it out... Am I missing something about this language that justifies this behavior? If I am, what can I do to stop it?
Thank you all.
You're seeing parts of the "Object" in Object Oriented Programming
$ts3 represents an Object containing all the information needed, along with some methods (or functions) that let you get data from the object. Some of these methods will do different things to the object itself, in order to retrieve additional data needed for a particular method call.
Consider the following simple Object:
Bike
color
gears
function __construct($color, $gears)
this.color = $color; this.gears = $gears
function upgrade()
this.headlight = true; this.gears = 10;
Now, when you first create it, it only has two properties:
$myBike = new Bike('red',5);
// $myBike.color = 'red';
// $myBike.gears = 5;
...but once you upgrade, properties have changed, and new ones are added.
$myBike->upgrade();
// $myBike.color = 'red';
// $myBike.gears = 10;
// $myBike.headlight = true;
Objects usually pass references rather than copying data, in order to save memory.
...but if you want to make sure that you're getting a copy that won't change (i.e. does not use data references to the $ts3 object), clone the variable.
$a = clone($ts3->clientList());
Be warned, this will effectively double the memory and processor usage for that variable.

Accessing a model from another unrelated model takes a very long time

I have a query in a CakePHP 3.0 table with a formatResults() method applied to it.
In order to carry out this calculation, data is required from a table in a different database (Currencies).
I am currently using this code to gather the data and pass it to the function:
$currencies = TableRegistry::get('Currencies');
$currencyValues = $currencies
->findByCurrno($options['currency'])
->cache('currency'.$options['currency']);
$currencyValues = $currencyValues->first()->toArray()
$query->formatResults(function ($results) use ($currencyValues) {
return $results->map(function($row) use ($currencyValues) {
$row['converted_earnings'] = $row['earned'] / $currencyValues['cur'.$row['currency']];
$row['converted_advances'] = $row['advances'] / $currencyValues['cur'.$row['currency']];
return $row;
});
});
The problem is, this code seems to take a very long time to execute, even though it is only iterating through a few hundred rows of data.
Further investigation revealed that if I do not collect the data from the 'currencies' table and instead declare $currencyValues as an array with fixed numbers the code takes a full second less to execute.
Through commenting out parts of the code, I have isolated this to be the cause of the problem:
$currencyValues = $currencies
->findByCurrno($options['currency'])
->cache('currency'.$options['currency']);
If I remove this part of the code then everything runs quickly, and as soon as I add it in (even if I do not use the data it returns and use the static array) the page takes far longer to load. It should be noted that this problem occurs whether or not I use the ->cache() method, and that the query itself reports that it takes 0-1ms in the sql dump.
My question is - why is this slowing down my execution so much and how can I stop it? It is pretty much a requirement that I use the data from this table for the operation, so I am looking for a way to speed it up.

php variable with site wide scope

I'm using Kirby CMS and creating a little snippet which limits by posts by what number they are, and what date they are. A way to create a 'autopost' system basically.
$today = new DateTime("now"); //Declare today
$startdate = new DateTime("2013-09-12"); //Declare startdate
$interval = $startdate->diff($today); //Find the difference between startdate & today
$latest = $interval->format('%a'); //Declare $latest variable to be that interval, formatted as integer
So I have that little bit which creates my $latest variable which I can then use to control the posts that are displayed.
My problem is, I don't want to have to change my $startdate on every different kind of page template I have, so I want to make it site-wide somehow.
I tried putting it as a snippet with Kirby's snippet() function but that doesn't work. The snippets must be being brought into the page after the snippet has already been run I guess.
How can I make my snippet apply to my whole site?
PHP doesn't have site-wide variables. The best you can do is put the assignments in a script, e.g. site.php, and have all your pages begin with require 'site.php'; to initialize these variables.

Showing progress bar while fetching data [duplicate]

I have this while loop, that basically loops through a lot of records in a database, and inserts the data in another:
$q = $con1->query($users1) or die(print_r($con2->errorInfo(),1));
while($row = $q->fetch(PDO::FETCH_ASSOC)){
$q = $con2->prepare($users2);
$q->execute(array($row['id'], $row['username'])) or die(print_r($con2-errorInfo(),1));
}
(The script has been shortened for easy reading - the correct one has a much longer array)
I would like to do this more graphical, and show a progress bar on how far it has went, instead of just seeing a page loading for a few minutes (there are ~20.000 rows in this one - I have tables with much more data)
I get that you could get the total number from the old database, and I could also easily put the current number into a variable like this:
$q = $con1->query($users1) or die(print_r($con2->errorInfo(),1));
$i = 0;
while($row = $q->fetch(PDO::FETCH_ASSOC)){
$q = $con2->prepare($users2);
$q->execute(array($row['id'], $row['username'])) or die(print_r($con2-errorInfo(),1));
$i++;
}
But now I need to actually fetch $i and display it - or something like it.
How is this "easily" done?
The code for the progress bar can either be in the same document as the while loop, or in another if easier.
You can do a "master" file that does an ajax to this first file to run a single query. You could get all the entry id's in this master file, and then pass it as a parameter to the second file that does a single query. Store these ids in a javascript array.
Create a function that does this, and when the first ajax is done, move to the second element of the id array, and do another ajax with a second parameter. That's how magento imports are done by the way :)
If you need further explanations, let me know, I tried my best to explain, but may have not been perfectly clear.
// you generate this javascript array using php.
// let's say you have all the ids that have to be processed in $Ids php array.
Ids = [<?php echo implode(',', $Ids); ?>];
function doAjax(i) {
$.ajax({ // using jquery for simplicity
'url': "ajax.php?id=" + Ids[i],
}).done(function(){
if ( i >= 0 ) {
// at the point you know you're at ((Ids.length-i)/(Ids.length) * 100) percent of the script
// so you can do something like this:
// $('.progressbar').css('width', ((Ids.length-i)/(Ids.length) * 100) + '%');
doAjax(i-1);
}
});
}
doAjax(Ids.length); // starting from the last entry
So, just to explain what this does. It starts by declaring a global javascript array that has all the ids that will need to be changed.
Then I declare a recursive ajax function, this way we can make sure that only one ajax runs at any single time (so the server doesn't blow up), and we can have a fairly accurate progress. This ajax function does the following:
Sends a request to ajax.php?id=xxx - where xxx is one of the ids in the javascript array.
In the file, we get the id ($_GET['id']), you take it from the old database, and insert it in the new one. This is only for one entry.
when the ajax is done, it goes to the done() function. Since we start the doAjax() function with the last element, we do the next iteration doAjax(i-1). Since we're going backwards in the array, we check if the key is positive. If it's not, the script will stop.
That's about it.
You can't. The php is first interpreted by the server and then send to the user as HTML-Code.
The only possibility would be creating a html-page and call the php-script with AJAX.

Increase cohesion by reusing a PHP function for multiple RSS feeds

My homepage contains weather for three cities around the world as displayed in the image
In the home page I declare 3 variables storing the RSS URL for each city
$newYorkWeatherSource = 'http://weather.yahooapis.com/forecastrss?p=USNY0996&u=f';
$londonWeatherSource = 'http://weather.yahooapis.com/forecastrss?p=UKXX0085&u=c';
$parisWeatherSource = 'http://weather.yahooapis.com/forecastrss?p=FRXX0076&u=c';
I pull identical tags out of the three URL's displayed above and use 3 identical functions apart apart from the variable passed into it.
Below shows the variable being passed into the function. Obviously other functions are used before $weather can be returned.
function new_york_current_weather($newYorkWeatherSource) {
// Get XML data from source
if (isset($newYorkWeatherSource)) {
$feed = file_get_contents($newYorkWeatherSource);
} else {
echo 'Feed not found. Check URL';
}
checkWeatherFeedExists($feed);
$xml = new SimpleXmlElement($feed);
$weather = get_dateTime($xml);
$weather = get_temperature_and_convert($xml);
$weather = get_conditions($xml);
$weather = get_icon($xml);
return $weather;
}
As I mentioned, I current repeat this function 3 times just replacing the $newYorkWeatherSource variable that is passed in the above example. Any ideas how I could reuse this function 3 times but yet pass in different URL to keep my homepage showing weather from the 3 cities? Ofcourse, it's easy to reuse the function if each city was represented on individual pages but the purpose is to keep them together for comparison.
Any ideas?
Thanks in advance.
As I mentioned, I current repeat this function 3 times just replacing the $newYorkWeatherSource variable that is passed in the above example. Any ideas how I could reuse this function 3 times but yet pass in different URL to keep my homepage showing weather from the 3 cities?
Maybe I'm entirely missing the point of your question, but are you asking how to rename the function and variables? Because, if so, it's just a matter of search and replace on the first few lines of the function...
function get_current_weather($rss_url) {
// Get XML data from source
if (isset($rss_url)) {
$feed = file_get_contents($rss_url);
} else {
echo 'Feed not found. Check URL';
}
// ...
Simply replace the city-specific functions with one starting out like this, and call it three times, one time for each specific city RSS feed URL.
From the comments:
but I'm just wondering what I will do with the 3 RSS URL variables because I can't replace rename them all to $rss_url as I will just be overwriting them until eventually the only URL will be Paris
I believe you may be suffering from a misunderstanding about PHP variable scope. Let's take this snippet as an example:
function bark($dog) {
echo 'The dog says ', $dog, ".\n";
}
$cat = 'meow';
bark($cat);
This code will emit The dog says meow. When you call the bark function with a variable, PHP takes a copy of the data* and passes it into the function as the variable name specified in the function. You don't need to name the variable the same thing both inside and outside. In fact, you can't** even see variables defined outside of a function:
function i_see_you() {
echo 'The dog heard the cat say ', $cat, ".\n";
}
$cat = 'meow';
i_see_you();
This code will emit The dog heard the cat say ., as $cat is out of scope here.
Getting back to the problem at hand, we still have three weather URLs.
$newYorkWeatherSource = 'http://weather.yahooapis.com/forecastrss?p=USNY0996&u=f';
$londonWeatherSource = 'http://weather.yahooapis.com/forecastrss?p=UKXX0085&u=c';
$parisWeatherSource = 'http://weather.yahooapis.com/forecastrss?p=FRXX0076&u=c';
All you need to do in order to make things work is:
echo get_current_weather($newYorkWeatherSource);
echo get_current_weather($londonWeatherSource);
echo get_current_weather($parisWeatherSource);
Inside the function, the proper variable with the proper name will have the proper data, and the right thing will happen.
*: PHP uses something called "copy-on-write", which does what you think it might do. It's completely safe to pass around variables containing large data. It will not consume unexpected amounts of memory. There's no need to use references. In fact, forget I ever said anything about references, you don't need them right now.
**: It's possible to see variables from the global scope by using the global keyword. Globals are bad practice and lead to spaghetti code. You might want to read more about variable scope in PHP.

Categories