Understanding MVC piece of code

I started with MVC in PHP, basically I get most of it, but still there is little piece of code I could not understand, it’s driving me crazy.
Here is the link from github: https://github.com/Inchoo/php-academy/blob/master/lesson-05/mvc-app/app/model/View.php

Below is confusing part:

public function render($name, $args =[] {
    ob_start();
    extract($args);
    include BP . "app/view/$name.phtml";
    $content = ob_get_clean();

extract(); - treats keys as variables names and values as variable values

ob_start() - turn on output buffering

But, what those functions actually do under the hood?
Are those functions required in MVC? I really can’t understand.
I really appreciate every answer/help!

I’m not familiar with that lesson (nor have I bothered to look at it :shifty: ) but I’ll give a try at explaining the function in my own words. Maybe working from inside → outside is easiest.

Say the included file has something like

echo '<span>' . $somevar . '</span>'; 

i.e. it expects the $somevar variable to have a value. It would be possible to assign a value to that variable inside of the function, but that would not be the best approach. By having the $arg argument default to an empty array it puts the responsibility of supplying the needed variables on whatever is calling the function. In MVC, it is the controllers job to do this, not the view.

Because the included file outputs, it could be that premature output could cause problems. The output buffer functions say “don’t output anything yet” then “OK, all ready, send output now”

The ob in ob_start stands for output butter. Everything that would normally get echoed directly to the browser will instead be directed to an internal buffer. $content = ob_get_clean(); then takes whatever is in the buffer and stores it into $content.

Later on a second page template containing all your html head information will be called with $content being passed to it resulting in a complete html page. The html will then be sent to the browser.

It is a fairly common approach to building up html pages. You might have a menu template as well as perhaps a side bar template. The html would be generated most or less independently and then stitched together in the master page.

The extract function just basically explodes the array and creates local copies of the array values which then get passed onto the template file.

<?php
$data = ['x'=>1,'y'=>2];
render($data);
function render($data) {
    // extract($data) is the same as
    $x = $data['x'];
    $y = $data['y'];
    ob_start(); ?>
<div>Some content <?php echo $x; ?></div>
<?php
    $content = ob_get_clean();
    echo $content;  // <div>Some content 1</div>
}
3 Likes

Hey man, I appreciate your help! Thank you for taking the time to help me!!!

A good question to ask when you wonder why certain things are a certain way, is: What if it wasn’t that way? What would be the drawback?

So we basically have three of those questions here:

What if this wasn’t in a class / function?

Let’s suppose that instead of having this in a class we would have this in a single file (aka transaction script).
It might look a little something like this.

Code:

$db = mysqli_connect(...);

ob_start();
extract($args);
include BP . "app/view/$name.phtml";
$content = ob_get_clean();

One clear drawback here is that the template is able to use everything that is in the scope of this file, including the database connection! But the whole point of MVC is that the view should be as dumb as possible, and should contain as little logic as possible. So giving it access to a database connection is not done, because that gives it way too much power.

Instead if you extract this code to a function or a method, the template only has access to variables that are defined within that function (and of course any (super)globals), and that fits better with the philosophy that the view should be as dumb as possible.

Note that within a method the template still has access to $this, which is a reference to the Renderer instance it’s being called from. There is a way around that. Brownie points to anyone who can tell me how :wink:

What if we didn’t use ob_start / ob_get_clean?
Suppose we have the following template:

<h1>Hello <?= $username; ?></h1>

And the render method doesn’t have ob_start() and ob_get_clean() but just echoes the template directly.
And we call our render method without any data:

Then as @ahundiak already said you would have to render all sub templates in the exact correct order, which can be hard or even impossible at times. So it’s better if you can just render anything you like and then stitch all the partials back together at the end.

What if we didn’t use extract?
Using extract means that all keys in $args will become variables in the current scope, so if you have an array like ['username' => 'rpkamp'] and you extract it, a variable $username is available in the render method, and thus also in your template. Without extract your template would look like this:

<h1>Hello <?= $args['username']; ?></h1>

Which works just as fine, except you’d need to type $args[''] for each and every variable, which would become annoying after a while. So the extract is just for convenience.

6 Likes

When I first started using PHP Frameworks I found just using the View values and not showing the Control array name was confusing. I accepted the new syntax shortcut and gradually found it a lot easier,

Many thanks for the detailed explanation.

I’ve always heard the saying

The View should never know that the Model exists and vice versa.

IMO, I don’t think ob_start and ob_get_clean should be used because if you implement MVC correctly, you don’t need to use these functions. IIRC, aren’t these functions for adding more stuff into the header without breaking PHP?

No. Almost a large majority of PHP functions aren’t required for MVC. Many MVCs run differently and have different styles, but they all follow the general MVC rules.

1 Like

Worth to know, thanks!

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.