Trying to access array offset on value of type null

Cupara
Member | 90
+
0
-

I'm struggling to get a specific news article to load after I click on it from the news page.

Here is everything so far.

The errors:

Warning: Trying to access array offset on value of type null in /public_html/temp/public_html-templates-news_detail.latte--086403e216.php on line 42
Warning: Trying to access array offset on value of type null in /public_html/temp/public_html-templates-news_detail.latte--086403e216.php on line 45
Warning: Trying to access array offset on value of type null in /public_html/temp/public_html-templates-news_detail.latte--086403e216.php on line 48

news.php file:

<?php
require_once 'vendor/autoload.php';
require_once 'includes/db.php';

$latte = new Latte\Engine;
$latte->setTempDirectory('temp');

if (isset($_GET['slug'])) {
    // Display individual news entry
    $slug = $_GET['slug'];
    $result = $conn->query("SELECT * FROM news WHERE slug = '$slug'");
    $news2 = $result->fetch_assoc();

    echo $latte->render(__DIR__.'/templates/news_detail.latte', ['news' => $news2]);
} else {
    // Display news listing
    $result = $conn->query('SELECT * FROM news ORDER BY created_at DESC');
    $news = $result->fetch_all(MYSQLI_ASSOC);

    echo $latte->render(__DIR__.'/templates/news.latte', ['news' => $news]);
}

news_detail.latte file:

{extends 'layout.latte'}

{block content}
    <h2>{$news['title']}</h2>
    <p>{$news['content']}</p>
    <p><small class="text-muted">{$news['created_at']}</small></p>
{/block}

And the corresponding php file from the temp folder:

<?php

use Latte\Runtime as LR;

/** source: /home/serenitydev/public_html/templates/news_detail.latte */
final class Template086403e216 extends Latte\Runtime\Template
{
	public const Source = '/home/serenitydev/public_html/templates/news_detail.latte';

	public const Blocks = [
		['content' => 'blockContent'],
	];


	public function main(array $ʟ_args): void
	{
		extract($ʟ_args);
		unset($ʟ_args);

		echo "\n";
		$this->renderBlock('content', get_defined_vars()) /* line 3 */;
	}


	public function prepare(): array
	{
		extract($this->params);

		$this->parentName = 'layout.latte';
		return get_defined_vars();
	}


	/** {block content} on line 3 */
	public function blockContent(array $ʟ_args): void
	{
		extract($this->params);
		extract($ʟ_args);
		unset($ʟ_args);

		echo '    <h2>';
		echo LR\Filters::escapeHtmlText($news['title']) /* line 4 */;
		echo '</h2>
    <p>';
		echo LR\Filters::escapeHtmlText($news['content']) /* line 5 */;
		echo '</p>
    <p><small class="text-muted">';
		echo LR\Filters::escapeHtmlText($news['created_at']) /* line 6 */;
		echo '</small></p>
';
	}
}

This is the last thing I need to get fixed then I can start working on the color scheme and get my site launched.

Thanks

Last edited by Cupara (2023-12-22 14:30)

David Grudl
Nette Core | 8142
+
+2
-

It tells you that $news2 is null, $result->fetch_assoc() returns null. This has nothing to do with Latte.

Cupara
Member | 90
+
0
-

It has to do with Latte because I have double-checked the code and the database. There are entries in the database so it has to be something in the code for Latte as I followed the Latte documentation to set up this iteration of my site.

David Grudl
Nette Core | 8142
+
+1
-

Did you try to dump $news2?

Cupara
Member | 90
+
0
-

LMAO, no I didn't. I didn't even think about that. I'll do that and see what happens. Thanks.

Cupara
Member | 90
+
0
-

So I did var_dump($news2); and it returns NULL but I'm not understanding why so I'm scouring through the docs for Latte to make sure I implemented the call right. I'm not used to using $result->fetch_assoc(); to fetch a single entry.

nightfish
Member | 473
+
+1
-

Cupara wrote:

So I did var_dump($news2); and it returns NULL but I'm not understanding why so I'm scouring through the docs for Latte to make sure I implemented the call right. I'm not used to using $result->fetch_assoc(); to fetch a single entry.

@Cupara
As David pointed out earlier, the problem is not in Latte. It is in these two lines:

$result = $conn->query("SELECT * FROM news WHERE slug = '$slug'");
$news2 = $result->fetch_assoc();

First problem: row 1 contains SQL injection vulnerability. Use prepared statements. If I am correct in the assumption that you are using mysqli class, it would be something like:

$statement = $conn->prepare("SELECT * FROM news WHERE slug = ?");
$statement->bindParam('s', $_GET['slug']);
$statement->execute();
$result = $statement->get_result();
$news2 = $result->fetch_assoc();

Second problem – condition in your SQL query probably does not match any rows – which is why $news2 is NULL.

Cupara
Member | 90
+
0
-

I don't see how it's not right as the slug is being passed otherwise the link wouldn't work properly. Maybe it's an issue in my .htaccess file and how it's parsing the link to prettify it.

Thanks for pointing out the prepared statement vulnerability. I haven't played with PHP in quite a few years and I'm forgetting so much stuff.

nightfish
Member | 473
+
0
-

@Cupara Well… you could dump your query with specific value for the slug (“SELECT * FROM news WHERE slug = ‘foobar’”) and then execute it in your database admin tool (Adminer, PHPStorm etc.) to see if it returns anything. Also maybe dump $_GET['slug'] to verify that it does not contain any trailing whitespace or other unwanted characters.

Cupara
Member | 90
+
0
-

Ok so I did a test, I grabbed it right from the database slug field for that specific page and updated my statement like so:

$statement = $conn->prepare("SELECT * FROM news WHERE slug = 'this-is-a-test-2'");

Then commented out:

$statement->bind_param('s', $slug);

And it displayed the data for just that entry like I want so I'm not understanding where the issue it.

Cupara
Member | 90
+
0
-

@nightfish I already did the dump of $_GET['slug'] and it's grabbing that right.

nightfish
Member | 473
+
0
-

@Cupara Running out of ideas here… What is the URL of the page, that should display news item with slug ‘this-is-a-test-2’?

Cupara
Member | 90
+
0
-

@nightfish I am running out of ideas as well.

Here is the link to it online: https://serenitydev.xyz/…-is-a-test-2

nightfish
Member | 473
+
0
-

@Cupara I don't get it. The URL https://serenitydev.xyz/news/this-is-a-test-2 has no query parameters, so $_GET['slug'] would not exist…

Cupara
Member | 90
+
0
-

@nightfish that's what I'm saying. I don't understand it at all.

nightfish
Member | 473
+
0
-

@Cupara However if you modify the URL to read https://serenitydev.xyz/news/this-is-a-test-2.php?slug=this-is-a-test-2, it displays the content correctly.

Cupara
Member | 90
+
0
-

@nightfish What?!? That makes no sense.

Also, could you check my db.php file to make sure I have no vulnerabilities, please? I'm trying to handle this on my phone as my ex-wife is driving down a bumpy road.

db.php:

<?php

$servername = "localhost";
$username = "XXXXXXXXXX";
$password = "XXXXXXXXXX";
$dbname = "XXXXXXXXX";

$conn = new mysqli($servername, $username, $password, $dbname);

if ($conn->connect_error) {
    die("Connection failed: " . $conn->connect_error);
}

function generateSlug($text) {
    $text = strtolower(trim($text));
    $text = preg_replace('/[^a-z0-9-]/', '-', $text);
    $text = preg_replace('/-+/', '-', $text);
    return $text;
}

Ignore the function as that isn't implemented yet.

Cupara
Member | 90
+
0
-

I'm honestly thinking about starting over from scratch for the 15th time. A lot of the startovers prior were because I was having way too many issues with specific template engine systems that I just didn't want to deal with but when I got to Latte, I liked it so much I wanted to deal with the issues I kept running into.

nightfish
Member | 473
+
0
-

@Cupara The db.php file looks okay-ish, but I would probably advise against using mysqli. It is preferable to use PDO or rather another higher-level database abstraction layer (nette/database, dibi/dibi, doctrine/dbal, nextras/dbal).

If you want to go full-Nette, I would advise using nette/database, as it can be tightly integrated with Nette.
And maybe base your project on nette/web-project and if you are new to Nette, go through the tutorial – https://doc.nette.org/en/quickstart. It will show you how to create a simple blog website (not dissimilar to your News/NewsDetail).

Cupara
Member | 90
+
0
-

@nightfish Thanks, I'll implement nette/database shortly.

Guess I'll scrap what I have and go full nette following the tutorial, thanks.