How does a block PHP file know the values POSTed through it's controller file?

Permalink
I have been studying and working with a form block (not the default form block) and have all but come to understand how it works so as to able to use it and have it do what I want.

However there is something within this block that is still a mystery to me and I would appreciate help understanding how Concrete5 does what it does here (what it does is no unique to this block in particular I am sure).

Specifically there is a my_contact_form.php file (which I will call the block PHP file) inside the /blocks/external_form/forms/ directory that uses variables that contain the values entered by a visitor into the form.
-
Like so...

<?php if (isset($name)) { ?>
   <input class="field" type="text" id="name" name="name" value="<?php echo $name; ?>" maxlength="70" size="40" />
<?php } else { ?>
   <input class="field" type="text" id="name" name="name" value="" maxlength="70" size="40" />
<?php } ?>


Do you see the "isset($name)" and "echo $name" PHP code? Basically $name is a variable that is initialized with the value of the "name" field in the form. That initialization happens inside the my_contact_form.php file inside the /blocks/external_form/forms/controller for the block.

It is initialized like so...

// $this->set() defined inside /concrete/libraries/block_controller.php
//    public function set($key, $value)
$name = $_POST['name'];
$this->set('name', $name);


So we have a variable inside the block PHP file called $name which comes from who knows where being initialized inside the controller through the set() method of the BlockController class.

In turn the set() method does this...

public function set($key, $value) {
   BlockController::$sets[$this->identifier][$key] = $value;
}


All well and good (well...a bit difficult to follow and understand the reason for all this redirection and multiple variable use but anyway...)

My problem is that I have no clue how Concrete5 takes the setting of $this->identifier with the key (i.e. $name) and the value (the value of the field as found inside the POSTED form values) and makes it a variable available to the block PHP file??

What kind of PHP trick allows it to do this?

How does Concrete5 take an array with the POSTed form values as set inside the BlockController class and then make those set values available inside the block PHP file when the block PHP file does not even address or otherwise access the identifier array within it's code anywhere.

It's like the POSTed values just appear out of thin air.

I mean PHP variables need to be created and initialized somewhere to be used...right? Concrete5 seems to pull them out of a hat somehow and presto...they are available for us even though I don't see anywhere in the code where they were...well...created!?

Anybody got any insight or clue as to how Concrete5 does this and how it uses the identifier array as found inside the BlockController class to do this?

I got to tell you that understanding the code beats anything else hands down with respect to understanding what Concrete5 does and how it works but man oh man...it is without a doubt the hardest PHP I have ever had to try and understand. Hardly any comments. Weirdly named variables. Variables named after a reason that no longer exists or applies. All kinds of such things.

This is the last area that I do not understand in the block code I have been studying. I mean I can use it just fine but I want to fully understand what is happening so that I can change things how I want them done in the future or fix things when and if they break.

Any help understand this "magical" variable use would be appreciated.

Thanks.

Carlos

PS. The block in question is an entirely custom block I have created (given all the changes I have made to it) but one which is based on block code I found on the forum so the essential problem described above is just...well...a Concrete5 mystery and not something that I did per se.

 
ScottC replied on at Permalink Best Answer Reply
ScottC
if you look around you can see that extract is called, you can look up what it does over on php.net herehttp://us2.php.net/manual/en/function.extract.php...

Another example of this is: if you look up Loader::element('element_file',$args)

if $args is an array then extract is called on it, which essentially takes an associative array and maps the hash or key to a variable and the value of each hash or key becomes the value of that variable/pointer.

If you look at the get sets, those are pulled from the adodb activerecord class where they do the getAttributeNames();

This causes then since the record is actually an adodb active record object it just calls the object properties and passes them to sets. If you look at the save method in the block controller you see how that "magically" happens.

So the sets are made, that's good :) So then you need to go over to the render function

function render($view) {
         $bv = new BlockView();
         $bv->setController($this);


then go to render there you'll see

extract($this->controller->getSets());

then:
ob_start();
            include($template);
            $outputContent = ob_get_contents();
            ob_end_clean();               
            print $outputContent;


note that include($template) the extract mapped again those sets out to where they are available in the view that you are rendering, the extract is further up in the method call.

One neat thing there you see is the ob_start() and ob_end_clean(). That's how they are doing the view caching there.

That help?
carlos123 replied on at Permalink Reply
Ah...hmmm...well...yeah...I think that helps LOL.

Seriously, for sure that helps Scott. That's exactly the kind of info I was looking at though of course it is rather obvious I think how your response points to the difficulty of understanding the underlying C5 way of doing things and it's code.

Unbelievable complexity.

Not saying your response was overly complex Scott. Only that it takes a lot to understand the code.

Time well spent though.

Thanks very much Scott! I'm going to spend the next little while studying and trying to follow what you said Scott (again...your explanation is fine..it's the nature and complexity of how C5 does things and where it does things underneath that is the problem I think).

Carlos
carlos123 replied on at Permalink Reply
Interesting that php.net strongly discourages use of extract() on untrusted input such as user entry into an online form.

Carlos
carlos123 replied on at Permalink Reply
Hmm...

I have been trying my hardest to find where an extract is used to create the forms POSTed values as variables into the name space.

Or where getSets() is used.

Or where render() is used.

And so on.

There are literally hundreds of places in the code where these functions are found.

Given the two files I have so far in my block...namely...

/blocks/external_form/forms/my_contact_form.php
/blocks/external_form/forms/controller/my_contact_form.php

Do you or anyone else know where the extraction takes place such that the values entered into the form and processed by the controller are then made available to the form block PHP file (the first one) again?

I can't find in the code where these things occur for the particular form I am using (such a place is certainly no unique to the form code that forms the base of my form block...I found the code base on the forum and is pretty standard as to where things are at...at least those things I can follow in the code).

Carlos
ScottC replied on at Permalink Reply
ScottC
since these things extend blockcontroller youd want to look at BlockController under libraries which extends COntroller which is also in there. If a method is defined in an extending class, the call to parent:: has to be made to continue up, otherwise if it isn't present in the extending class it'll call the parent class automatically and that classes parent class until it finds the function before dying and saying it isn't found.
carlos123 replied on at Permalink Reply
Thanks Scott. I hadn't looked inside the Controller class just the BlockController class. I'll look inside Controller next.

Carlos
carlos123 replied on at Permalink Reply
Scott,

Do you or anyone else here know how I might be able to force Concrete5 to give me a trace call back? To output where in the code it ran before encountering an error?

That would help me immensily figure out where something is coming from instead of searching all over the files in a hit or miss manner.

I suppose I can just look up how to force a traceback as a PHP thing that can be done on any PHP code and just...well...force C5 to give me one but I thought I would ask here in case anyone had ever done that with C5 before.

Carlos
jordanlev replied on at Permalink Reply
jordanlev
Install xdebug:http://www.xdebug.org/

Kind of a pain to set up and install, but once you do it is an invaluable tool for the kinds of things you're wanting to do here. I wouldn't be able to solve half the problems I have (in C5 or any other complicated framework) without using a step debugger. The debugger can be REALLY hard to set up right, but even if you can't get that, the stack trace is a basic feature of xdebug that should be very easy to install.
carlos123 replied on at Permalink Reply
Thanks for reminding of that particular debug helper Jordan.

Yes...it is a pain to use but I once did use it (wasn't all that satisfied with it so I forgot about it) but now that the need for it's type of capability is here again (for me at least) I'll see if I can insert it into C5 to better "look" at what is being done where.

I don't know why I didn't think about using a backtrace before starting this thread. Duh...

Carlos
carlos123 replied on at Permalink Reply
By the way...now that I installed xdebug and can finally "see" what is going on where...

I have to say that installing xdebug under Ubuntu Linux is an absolute piece of cake. At a command prompt...

~$ sudo apt-get install php5-xdebug


Then edit the file /etc/php5/conf.d/xdebug.ini and add the following two lines at the end.

display_errors = On
html_errors = On


Finally, restart Apache for change to take affect...

~$ sudo /etc/init.d/apache2 restart


After the above steps just go into the code anywhere you fancy and insert a call to a non-existent function like "nothing();" and voila!

A backtrace showing the functions that were run to that point show up.

How sweet it is! I mean Linux. It's great developing locally on the same operating system as used by many web hosting companies.

Carlos
carlos123 replied on at Permalink Reply
Shucks. It appears that my addition of a call to a non-existant function does not cause xdebug to be activated when that call is inside the action file of a form.

I'll have to play around with it some more but I sure hope I can get xdebug to show me it's pretty table of function calls from within the controller of a form block somehow.

Carlos
carlos123 replied on at Permalink Reply
There we go...

Instead of using a call to a non-existant function to force an error (and a backtrace thereby) I just included the following function call in the code of the controller where I want the backtrace to show up instead.

xdebug_print_function_stack();

Carlos