0

I am trying to display on an Wordpress webpage certain values contained in a CSV file (which is hosted online) by using PHP. I update this CSV file every X hours via a Python FTP code.

The webpage has the following permalink https://example.com/user/id/. The CSV file is hosted on the website and is made up by multiple rows with the same number of columns. Example:

username,user_id,value1,value2,value3,value4
mario,1,1000,1100,1200,1300
luigi,2,2000,2100,2200,2300
...

I would like to parse the CSV file and display values selectively depending on the id of the user.

I can get the id of the user from the permalink of the page with the following function:

<?php

$permalink = get_permalink();  // $permalink is now 'https://example.com/user/1/'

$permalink = trim($permalink, "/"); // $permalink is now 'https://example.com/user/1'

$user_id = substr($permalink, strrpos($permalink, '/') + 1); // $user_id is now '1'

?>

Now that I have the user_id, I would like to show the values in its row. For example, if user_id is "1" (second column of the CSV file), then I would like to display on the webpage the following output:

Value 1 is 1000
Value 2 is 1100
Value 3 is 1200
Value 4 is 1400

To display the values in the webpage I tried to use something like this:

$csv_url = "https://example.com/path/filename.csv"
$f = file_get_contents($csv_url);
$items = explode(',', $f);
foreach($items as $item){
    $user = explode(",", $item);
    // $user[0] - will contain the first number
    // $user[1] - will contain second
    // $user[3] - will contain third
}

But this is not working as intended because it cannot separate the rows.

What PHP code would achieve the result I would like to get?

EDIT New code is:

<?php
$permalink = 'https://example.com/user/1/';  // $permalink is now 'https://example.com/user/1/'

$permalink = trim($permalink, "/"); // $permalink is now 'https://example.com/user/1'

$user_id = substr($permalink, strrpos($permalink, '/') + 1); // $user_id is now '1'

$csv_url = "https://example.com/path/users.csv"
$f = file_get_contents($csv_url);
$lines = explode("\n", $f);
foreach($lines as $line){
    $user = explode(",", $line);

    if ($user[1] !== $user_id) {
        continue;
    }

echo "<strong>Points:</strong>"
echo "Value 1: " . $user[2]
echo "Value 2" . $user[3]
echo "Value 3: " . $user[4]
echo "Value 4: " . $user[5]
echo "Value 5: " . $user[6]

}
?>
Nox19
  • 35
  • 9
  • No, no, no ... use `fgetcsv($h)` and `$h=fopen($file,'r')` etc. Just look up PHP `fgetcsv()` - I am sure you will find some examples. Don't use explode as it doen't know about encasing `"foo, bar"` which in `fgetcsv` is 1 column as it should be and in `explode` its `['"foo', 'bar"']` (all messed up) not to mention escaping `\"` etc. – ArtisticPhoenix Mar 28 '19 at 19:00
  • @kuh-chan I had a look at that question, but I have no idea how to select the row based on the `user_id` – Nox19 Mar 28 '19 at 19:02
  • You really don't want to manually parse CSV, it's not as simple as it looks at first glance. You can also use `SplFileObject` (which is what I personally use) too if you want an OOP interface to work with. I have worked extensively with CSV's over the last 5 years, so you can trust what I say. – ArtisticPhoenix Mar 28 '19 at 19:04

5 Answers5

1

Try exploding by the newline character first, and afterwars the , sign. Like this:

$csv_url = "https://example.com/path/filename.csv"
$f = file_get_contents($csv_url);
$lines = explode("\n", $f);
foreach($lines as $line){
    $user = explode(",", $line);


    // $user[0] - will contain the username
   // $user[1] - will contant id
}
Mathias Dam
  • 107
  • 1
  • 4
1

why not just use the csv parser built into php then you can use a proper array or object to do this easily?

if (($handle = fopen("test.csv", "r")) !== false) {
    while (($data = fgetcsv($handle, 1000, ",")) !== false) {
        for ($c = 0; $c < count($data); $c++) {

        }
    }
}
Lulceltech
  • 1,662
  • 11
  • 22
1

Thanks @Tim - Yes I did steal part of your answer, but I am lazy like that.

I just wanted to add this too it.

 if (false !== ($handle = fopen("test.csv", "r"))) { //constants go on the left, which prevents accidental assignment `if(false = $foo)` throws an error, the other way sets foo to false. 
    //I do declare!! 
    $headers = false;
    $num_headers = 0;

    while (($data = fgetcsv($handle, 1000, ",")) !== false) {

        //get headers from the first row
       if(!$headers){
           $headers = array_unique($data);
           $num_headers = count($headers);
           continue; //skip to next row - just because depending how you handle "below" you may need it.
       }else if( count($data) != $num_headers){
           //rows here have missing or extra delimiters or duplicated header names
           //you should ignore, throw an error or try to fix them etc. depending on your needs.
       }else{
           //combine headers and data
           $row = array_combine($headers, $data);
           //now you can use associative keys
           echo $row['username'];
           echo $row['user_id'];
           //... etc
       }
    }
}

Above I am showing how you can combine a header row with a data row, to allow use of the headers as keys in the data array. This prevents positional errors that can happen if the order of the CSV changes (and it's just easier to read and use ) etc...

array_combine will bomb out if the two arrays are different length, this can happen because of errors in the CSV file such as an extra or missing delimiter. We need to catch (or test for this case) and act accordingly. It can and does happen for various reasons that the file will have some error like this in it, best to not be surprised by it.

All your headers are uniquely named so I didn't worry about it too much but remember when using them as array keys, the keys must be unique therefor so must your headers.

Hope it helps!

ArtisticPhoenix
  • 21,464
  • 2
  • 24
  • 38
  • Always happy to help, thanks for going more in detail for him than I did, you did a good job of demoing further concepts to him :) – Lulceltech Mar 28 '19 at 19:25
  • @TimHinz - Sure, I work with CSV's every day, I actually have a CSV class built around SplFileObject that can map the CSV data to a class. Basically it has fetch modes like PDO. But it's technically the intellectual property of my employer and it has some business specific stuff in it. I'll have to strip it down someday and put it on Git as I see questions about CSV all the time and I haven't found a good package like mine fore it out there. Not that I have looked much... – ArtisticPhoenix Mar 28 '19 at 19:28
  • Funny you say that so do I, I'm an integration engineer myself for a company I won't name. I spend all day working with CSV, XML, and Excel files creating ways to parse them in any given format or arrangement of data. – Lulceltech Mar 28 '19 at 19:30
  • We aggregate public Lawsuit data, and then sell a B2B only search service, we do apx 180million searches a day on it. And the reports are all in CSV (we have 6 different formats of reports) and 3 other related search services with their own reports etc. I am the CIO of the company (but it's small only 5 employees) - currently I am the only developer as our Junior developer took a full time position. So they keep me busy. – ArtisticPhoenix Mar 28 '19 at 19:33
  • Well that's actually really neat, it was a pleasure meeting someone who's job is similar to mine :)! – Lulceltech Mar 28 '19 at 19:34
  • Sure, I built (or rebuilt) our entry system, from some of the data scrapers to the structure of the Database, to the Queuing system. Mostly we deal with CSV files (you guessed it) clients send them we run them and return reports on that run. But currently I am building a Real Time JSON (RESTfull) API for it, so it's exciting for me... :-). You understand I can't get too specific on technology etc. But it's nice meeting you too. – ArtisticPhoenix Mar 28 '19 at 19:37
0

I managed to get it working with this code:

<?php
$permalink = get_permalink();  // $permalink is now 'https://example.com/user/1/'

$permalink = trim($permalink, "/"); // $permalink is now 'https://example.com/user/1'

$user_id = substr($permalink, strrpos($permalink, '/') + 1); // $user_id is now '1'

$csv_url = "https://example.com/path/users.csv";
$f = file_get_contents($csv_url);
$lines = explode("\n", $f);

foreach($lines as $line){
    $user = explode(",", $line);

    if ($user[0] == $user_id) {
        break;
    }

}
echo "<strong>Points:</strong> <br>";
echo "Value 1: " . $user[2] . "<br>";
echo "Value 2: " . $user[3] . "<br>";
echo "Value 3: " . $user[4] . "<br>";
echo "Value 4: " . $user[5] . "<br>";
echo "Value 5: " . $user[6] . "<br>";
?>
Nox19
  • 35
  • 9
  • This site is an educational resource for researchers. Managing to craft a working solution can wildly differ from "the right way to do it". Remember, volunteers do not post just to help you; we post our best advice for the benefit of thousands of future researchers. I hope that you will help future researchers to find the best approach by upvoting and marking an accepted answer. – mickmackusa Mar 29 '19 at 04:36
  • @mickmackusa I am aware. The only reason I am hesitant for now to select yours or another of the CSV parsing answers is because of the vulnerability of fopen() to injections (CRLF). – Nox19 Mar 29 '19 at 10:41
  • Please enlighten me by providing a reputable document discussing this matter. From what I see you have accepted an answer that defies the itemized advice that my answer provides. – mickmackusa Mar 29 '19 at 13:56
  • @Artistic please extend to me any knowledge that you have regarding the insecurity of `fopen()`. This is news to me. – mickmackusa Mar 29 '19 at 14:07
0

Yes, you should use csv-purposed php functions on csv files.

No, you don't need to use yoda-speak in conditions with appropriately written parentheses.

No, you don't have to declare the delimiter parameter in fgetcsv() because comma is the default. If you want to set the length parameter, there is some performance benefit stated in the documentation page.

Yes, you should most definitely break the loop immediately after finding your match so that you don't kill efficiency.

Yes, you should extract/consume the header before looping to avoid unnecessary conditionals and temporary variables.

Untested snippet:

if (($handle = fopen("test.csv", "r")) !== false) {
    $header = array_slice(fgetcsv($handle, 1000), 2);
    while (($row = fgetcsv($handle, 1000)) !== false) {
        if ($row[1] == $id) {
            foreach (array_slice($row, 2) as $index => $value) {
                echo ucfirst(preg_replace('~\D+\K~', ' ', $header[$index])) , ' is ' , $value;
                break;
            }
        }
    }
    fclose($handle);
}
mickmackusa
  • 43,625
  • 12
  • 83
  • 136