Grab and Explode Data - php

I'm sort of new to PHP, and I need some help on exploding data from a file. The file in question is: http://data.vattastic.com/vatsim-data.txt
Basically, I need to get the data under the !CLIENTS: section (near the bottom). With this data, I need to explode it and get the info between each :.
I have tried with this code, but it gives me a variable offset error (Undefined offset: 3)
$file = file("http://data.vattastic.com/vatsim-data.txt");
foreach($file as $line)
{
$data_record = explode(":", $line);
// grab only the data that has "ATC" in it...
if($data_record[3] == 'ATC' && $data_record[16] != '1' && $data_record[18] != '0'&& stristr($data_record[0],'OBS') === FALSE)
{
rest of code here...
}
}
If someone could help me with this, I'd greatly appreciate it.

This happens because you are trying to explode rows like this:
; !GENERAL contains general settings
When you explode that line, you your $data_records looks like this:
Array (
[0] => ; !GENERAL contains general settings )
Quick solution:
$file = file("http://data.vattastic.com/vatsim-data.txt");
foreach($file as $line)
{
if(strpos($line,';') === 0) continue ; // this is comment. ignoring
$data_record = explode(":", $line);
$col_count = count($data_record);
switch($col_count) {
case 42: // columns qty = 42, so this is row from `clients`
// grab only the data that has "ATC" in it...
if($data_record[3] == 'ATC' && $data_record[16] != '1' && $data_record[18] != '0'&& stristr($data_record[0],'OBS') === FALSE)
{
rest of code here...
}
break;
default:
// this is other kind of data, ignoring
break;
}
}

Another solution is to use regular expressions and look for !CLIENTS: section. This would also work in the case that the CLIENTS have more or less than 42 columns in the future
$file = file_get_contents ("http://data.vattastic.com/vatsim-data.txt");
$matches = null;
preg_match ('/!CLIENTS:\s\n(.*)\n;/s' , $file, $matches );
if($matches)
{
$client_lines = explode("\n", $matches[1]);
foreach ($client_lines as $client)
{
$data_record = explode(":", $client);
if($data_record[3] == 'ATC' && $data_record[16] != '1' && $data_record[18] != '0'&& stristr($data_record[0],'OBS') === FALSE)
{
//rest of code here...
}
}
}

Related

explode() expects parameter 2 to be string, array given

I have designed below code where i am using explode to explode below data,
"10.74.10.1", "10.75.10.132"
however i getting below error
"explode() expects parameter 2 to be string, array given in line.."
Can someone please suggest whats wrong in my code.
This is my full code:
public function pagesviewlogsAction()
{
// Checks authorization
$this->acl->doCheck($this);
-- language: lang-html --> // Validates request
$requestObj = new PagesviewlogEventRequest();
$requestObj->userid = (Utils::validatePattern(Utils::REGEXP_SECLOGS_USERID, (($json->userid) ?? FALSE) )) ? $json->userid:NULL;
$requestObj->clientip = array();
//if (isset($json->clientip) && $json->clientip != '' && $json->clientip != NULL) {
if (isset($json->clientip) && is_string($json->clientip)){
$tmp = explode(',', $json->clientip);
foreach ($tmp as $key => $ipValue) {
$requestObj->clientip[] = (Utils::validatePattern(Utils::REGEXP_SECLOGS_IP, ((trim($ipValue)) ?? FALSE) )) ? trim($ipValue):NULL;
}
}
}
foreach (get_object_vars($requestObj) as $key => $value) {
switch ($key) {
case 'clientip':
// ...
break;
default:
// Other cases
if ($value === FALSE) {
return new JsonModel([
'status' => 'FAILED',
'errorField' => $key,
'message'=> 'Parameters "' . $key . '" is missing or invalid.',
'data' => NULL
]);
}
break;
}
}
}
}
You condition :
if (isset($json->clientip) && $json->clientip != '' && $json->clientip != NULL)
can return true with an array.
better use something like this :
if (isset($json->clientip) && is_string($json->clientip))
The function explode() will convert a string to an array using a given separator, in your case ","
Since $json->clientip is already an array, the simple(not the best) solution is to change the code to:
$requestObj->clientip = array();
if (is_array($json->clientip)) {
foreach ($json->clientip as $key => $ipValue) {
$requestObj->clientip[] = (Utils::validatePattern(Utils::REGEXP_SECLOGS_IP, ((trim($ipValue)) ?? FALSE) )) ? trim($ipValue):NULL;
}
} else {
//handle the other option here. like string or object
}
and it depends on the source of the $json->clientip to make sure you have the correct approach in case you don't receive an array.
Exactly as it's telling you,
"10.74.10.1", "10.75.10.132" is an array. Explode requires a string because it creates an array based on the seperator ,
Try a var_dump() on your $json->clientip and see what it looks like, you may have to re-work your code a bit here.
Can I propose a possibility? I would check for both possible cases. If array execute one way, if string execute your explode.
if (!isset($json->clientip)) {
// thow exception or return call
}
$requestObj->clientip = [];
if (is_array($json->clientip)) {
array_walk($json->clientip, function($ipValue) use(&$requestObj) {
$ipValue = trim($ipValue);
$requestObj->clientip[] = (Utils::validatePattern(Utils::REGEXP_SECLOGS_IP, (($ipValue) ?? FALSE) )) ? $ipValue:NULL;
});
} else if (is_string($json->clientip)) {
// execute your explode
}
Also I would advice to check on Marshallers to help you parse logic in your code to tidy it up more instead of leaving it all into the same place. So your Utils::validatePattern could be a Marshaller in my opinion

Remove a line from file if it exists

I'm getting used to PHP and trying to remove a line from a file (if it exists) and resave the file.
So if I had the file
user1
user2
user3
user4
I could use
if(existsAndRemove("user3")){
do thing
}
I've tried using code similar to the code below but it sometimes bugs out and will only remove a line if it is last in the file. I have no idea how to fix this.
$data2 = file("./ats.txt");
$out2 = array();
foreach($data2 as $line2) {
if(trim($line2) != $acc) {
$out2[] = $line2;
}
}
$fp2 = fopen("./ats.txt", "w+");
flock($fp2, LOCK_EX);
foreach($out2 as $line2) {
fwrite($fp2, $line2);
}
flock($fp2, LOCK_UN);
fclose($fp2);
}
}
Any help at all would be greatly appreciated, and i would also appreciate if you could explain the code too so I could easier learn from it!!
Thank you.
If the file size is small enough that you're not worried about reading it all into memory, you could do something more functional
// Read entire file in as array of strings
$data = file("./ats.txt");
// Some text we want to remove
$acc = 'user3';
// Filter out any lines that match $acc,
// ignoring any leading or trailing whitespace
//
$filtered_data = array_filter(
$data,
function ($line) use ($acc) {
return trim($line) !== $acc;
}
)
// If something changed, write the file back out
if ($filtered_data !== $data) {
file_put_contents('./ats.txt', implode('', $filtered_data));
}
Something like this might work:
function remove_user($user) {
$file_path = "foo.txt"
$users = preg_split("[\n\r]+", file_get_contents($file_path));
foreach ($users as $i => $existing) {
if ($user == $existing) {
$users = array_splice($users, $i, 1);
file_put_contents($file_path, implode("\n", $users));
break;
}
}
}
Should be much easier since you're already using file():
$data2 = file("./ats.txt", FILE_IGNORE_NEW_LINES);
unset($data2[array_search('user3', $data2)]);
file_put_contents("./ats.txt", implode("\n", $data2));
Or to check if it exists first:
$data2 = file("./ats.txt", FILE_IGNORE_NEW_LINES);
if( ($key = array_search('user3', $data2)) !== false ) {
unset($data2[$key]);
file_put_contents("./ats.txt", implode("\n", $data2));
}

PHP how to return false with file() for an empty .csv file?

Why do I always get a array with one item of empty string for an empty .csv file?
$content = file('products.csv');
print_r($content);
result:
Array ( [0] => )
Can I return false so that I know there is nothing in the csv file?
This seems like ad-hoc behaviour particular to your taste (no problem with that at all). Which means, you should probably create a wrapper function for this.
function contentIsNotEmpty($content) {
$isEmpty = empty($content) || (count($content) == 1 && empty($content[0]));
return $isEmpty ? false : $content;
}
EDIT: Incorporated #scrowler's feedback, and Michael J. Mulligan's.
A single line test should get you the result:
$empty = empty($content) || (count($content) === 1 && empty($content[0]));
The following should avoid the "fake empty":
$empty = empty($content) || (count($content) === 1 && $content[0] === '');
If you need a re-usable function, and prefer, as you stated, to get an array or nothing, this may be helpful:
function get_file($file_name = null, $strict = false) {
$return = null;
if(!empty($file_name) && is_readable($file_name)) {
$contents = file($file_name);
if(
!empty($contents)
&& (
count($contents) > 1
|| (
!empty($contents[0])
|| ($strict && $contents[0] !== '')
)
)
) {
$return = $contents;
}
}
return $return;
}
I mean, we could get all kinds of creative and iterate over lines, etc. But I think you get the idea.
If you want to get a CSV file, I would suggest using a method like fgetcsv() (repurposed):
function getcsv($file_name, $headers = array(), $delimiter = ',', $enclosure = '"', $escape = "\\" ) {
$contents = array();
$get_headers = $headers === FALSE;
$headers = is_array($headers) ? array_values($headers) : array();
if(!empty($file_name) && is_readable($file_name)) {
$row = 0;
if (($handle = fopen($file_name, "r")) !== FALSE) {
while (($data = fgetcsv($handle, 0, $delimiter, $enclosure, $escape)) !== FALSE) {
if($get_headers && empty($headers)) {
$headers = $data;
continue;
}
foreach($data as $i => $col_value) {
$col_name = isset($headers[$i]) ? $headers[$i] : $i;
$contents[$row][$col_name] = $col_value;
}
$row++;
}
fclose($handle);
}
}
return $contents;
}
Note, above is not tested, just a quick draft, and I am going to bed. I'll edit it tomorrow if need be.
Finally, if you are getting a single line, with white-space, and this validates as "empty" in your eyes, simple test it after a trim:
$empty_line = trim($content[$line_num]) == '';
Not sure what else to tell you. I think we have equipped you with quite a few tools and ways to validate this situation. Best of luck.
try this
$content = file('products.csv');
if(!empty($content)){
print_r();}{
else{
// Do something if no content
}

Using nested foreach loop on same array

Is it ok to loop array again in nested loop and also change the array?
I've an URL's array with entries(as array key) of either an URL or domain:example.com
In case of this entry : domain:example.com I want to remove all URLS containing example.com as domain:
foreach (array_keys($urls1) as $line) {
if (preg_match('/domain:(.*)/i', $line, $matches)) {
$domain = $matches[1];
foreach (array_keys($urls1) as $line2) {
if ($url_domains[$line2] == $domain) {
unset($urls1[$line2]);
}
}
}
}
There is no problem looping over it a second time, however you will get yourself and your code into a big knot if you start removing items. My suggestion would be to save a copy and modify that.
This is not ideal, but I'm not sure what you wish to do.
//Make a copy of your array
$URLCopy = $urls1;
foreach (array_keys($urls1) as $line) {
if (preg_match('/domain:(.*)/i', $line, $matches)) {
$domain = $matches[1];
foreach (array_keys($urls1) as $line2) {
if ($url_domains[$line2] == $domain) {
unset($URLCopy[$line2]);
}
}
}
}
I ran into a similar problem and making a copy of the array was the answer. This was my problem:
If a particular text string existed towards the beginning of the file and (an array of approximately 80 members) matched a string towards the end of the file, I had to remove three lines towards the end. The problem that happened when I didn't use a copy is that the index would reset from 30, back to 9, and this caused me some issues.
This is what worked for me.
$rowCopy = $row
foreach($row as $index => &$line) {
////previous code
if ($line[0] === "NM1" && $line[1] === "77") {
//read through the $row array and find the NM*85 string
foreach ($rowCopy as $index2 => $lineT) {
if ($s = strpos($lineT, "NM1*85") !== false) {
$npiTest = explode("*", $lineT);
if (strcmp(preg_replace("/[^0-9,.]/", "", $npiTest[9]), $line[9]) === 0) {
// $line = false;
$index--;
unset($row[$index + 1]);
$index++;
unset($row[$index + 1]);
$index++;
unset($row[$index + 1]);
$erased = $erased + 3;
$index++
}
}
}
}
}

Find specific text until it is found it stops

I was wondering how to do it, this code will as you know get specific line, now I need it to read until a specific text like 55 and stops reading from there. As you can see the log contains some whitespace so what function can I use to read until the code 55?
$row['MtID'] = A unique ID to specify the line where the result is.
So for example the log of the result will be
MM3,67624563 (Unique ID (MtID),233262345599,http://mywebsite.com:8080/web/mm3_pixel.php?sspdata=ams1CIv44qa26LGkchACGKqShLrCtZieSyINNDEuMTkwLjg4LjIwOCgB&vurlid=993211,http://mywebsite.net/sspx?id=69171&sspdata=ams1CIv44qa26LGkchACGKqShLrCtZieSyINNDEuMTkwLjg4LjIwOCgB >> OK
,55
$logfile = file("https://myweb.com/Pixel-Full.php?file=".$country."/".$today."-pixel-response.log");
foreach($logfile as $line_num = > $line) {
if (strpos($line, $row['MtID']) !== false) {
$getresult = strstr(htmlspecialchars($line), 'http://');
echo "<td>".$getresult."</td>";
}
}
This system goes like this, a user request something and nothing found, so on our log, it will post the error link requested by user and the error code for us to know what problem it was. So once the system reads the line and continue to read other line as well until it found the code, it stops
$startline = count($logfile)+1;
foreach($logfile as $line_num => $line) {
if (strpos($line, $row['MtID']) !== false) {
$startline = $line_num;
$getresult = trim(strstr(htmlspecialchars($line), 'http://'));
if (strpos($getresult, ",55") !== false) {
$getresult = substr($getresult,0,strpos($getresult, ",55")+3);
break;
}
}
if ($line_num > $startline) {
$getresult .= trim(htmlspecialchars($line));
if (strpos($getresult, ",55") !== false) {
$getresult = substr($getresult,0,strpos($getresult, ",55")+3);
break;
}
}
}
echo "<td>".$getresult."</td>";
You can use the FILE_SKIP_EMPTY_LINES flag in the file call to skip empty lines and then use array_slice to get the part of the array you need.
$file = array_slice(file("https://myweb.com/Pixel-Full.php?file={$country}/{$today}-pixel-response.log", FILE_SKIP_EMPTY_LINES), 0, $row['MtID']);
foreach($file as $line) {
$result = strstr(htmlspecialchars($line), 'http://');
echo "<td>{$result}</td>";
}
It looks like stopping the execution when a positive match is the biggest trick here. This can be done with a break. (http://php.net/manual/en/control-structures.break.php)
//get the file as a string
$logfile = file_get_contents("https://myweb.com/Pixel-Full.php?file=".$country."/".$today."-pixel-response.log", false);
//make up some rows and catch the false
if ($logfile !== false) {
$logrows = explode("\n", $logfile);
//loop
foreach($logrows as $line) {
if (strpos($line, $row['MtID']) !== false) {
$getresult = strstr(htmlspecialchars($line), 'http://');
echo "<td>".$getresult."</td>";
break;
}
}
}
else echo "Log resource unavailable";
//some memory clearing
unset($logfile);
unset($logrows);
I would suggest for matching sanity that you make sure that the logging format makes the MtID variable something that wouldn't be found in the log text unless its a positive match. Using a UUID or a specific ID format would work.
As you regarded, sometimes, the log line may be divided into three lines while in some other times it is only one line:
// Three lines
http://www.yourwebsite.com/error/33/happycodingneverending&errorcode=33,
succcess=false;
,55
// one line
http://www.yourwebsite.com/error/33/happycodingneverending&errorcode=33,succcess=false;,55
In this case you just have to made a little modification to your code, which, works perfect with only one line example to works with that three lines as follows:
foreach($logfile as $line_num = > $line) {
if (strpos($line, $row['MtID']) !== false) {
$getresult = strstr(htmlspecialchars($line), 'http://');
if(!$getresult){
$theExactLine = $line_num - 2;
$getresult = $logfile[$theExactLine];
}
echo "<td>".$getresult."</td>";
break; // to stop looping.
}
}

Categories