As I have said, my issue is not with Symfony in particular. I have no interest in using it because the PAC architecture is problematic. I’ve still yet to see you attempt to answer the question “Why is PAC better than MVC?”
OOP doesn’t mean methods suddenly became off limits. We’re getting back into yu-huh/nu-uh territory, so I’m going back to my original request: Please provide step by step refactoring that illustrates your case. When you (if you) do so, please approach it as if you were reporting a software bug. Start by showing a dead simple example, as simple as you can make it, that demonstrates the issue, then show your step by step refactoring to resolve that issue.
Unfortunately I had hoped anyone participating in a discussion at this level would have an understanding of the difference between procedural and oop and understand the problems created by introducing dependencies in code.
Let’s take a step back, away from frameworks, away from everything back to the fundamental difference between OOP and Procedural programming.
Let’s create a basic function that formats a unix timestamp as a date using a specified format:
function formatDate($format, $timestamp) {
return date($format, $timestamp);
}
$formattedDate = formatDate('d/m/Y', time());
In OOP this would be expressed as:
class Date {
private $timestamp;
public function __constuct($timestamp) {
$this->timestamp = $timestamp;
}
public function format($format) {
return date($format, $this->timestamp);
}
}
$date = new Date(time());
$formattedDate = $date->format('d/m/Y');
OOP is more code, but there is a key difference: encapsulation. What this means is that the part of the code which is concerned with formatting the date is not concerned with locating the timestamp.
As such, the constructed $date object can be passed around the system with along with its state.
The procedural method is stateless this means it needs to be passed all its parameters every time. The knock on effect here is that anywhere that calls the formatDate() function has the responsibility of providing both the timestamp and the date format.
A procedural template would look like this:
<p>The date is: <?php echo formatDate('d/m/Y', $timestamp); ?></p>
Whereas the OOP version would look like this:
<p>The date is: <?php echo $date->format('d/m/Y'); ?></p>
The practical difference here is minimal, isn’t it? In both cases, a single variable has been bound to the template.
However, from a flexibility perspective, it’s not great. formatDate() can only take timestamp as a parameter. What if I had a system where some of the dates were in a string format of ‘YYYY-DD-MM’?
With the procedural method, I’d have to create a stringToTimestamp() method like so:
function stringToTimeStamp($string) {
list($y, $m, $d) = explode('-', $string);
return mktime(0, 0, 0, $m, $d, $y);
}
And every time I came across a date, I’d have to convert it:
<p>The date is: <?php formatDate('d/m/Y', stringToTimeStamp($strDate)); ?></p>
The problem? I had to alter the template* to know that the date is coming in as a string not a timestamp.
- Yes, this could be done when it’s assigned to the template, but that moves the problem to the binding logic. The conversion must be done somewhere.
OOP avoids this problem entirely:
class StrDate {
private $string;
public function __construct($string) {
$this->string = $string;
}
public function format($format) {
list($y, $m, $d) = explode('-', $this->string);
return date($format, mktime(0, 0, 0, $m, $d, $y));
}
}
I can now pass a StrDate object to the template and the OO template works without being changed, as does the code which is binding variables to the template.
Hopefully that’s a simple enough demonstration of OOP vs Procedural. Now, back on track… your code is procedural and not oop not because it’s using methods, but because your class is self-configuring and stateless.
Consider this:
class Date {
private function getCurrentDate() {
return time();
}
private function format($date, $format) {
return date($format, $date);
}
public function output() {
return $this->format($this->getCurrentDate(), 'd/m/Y');
}
}
[b]This is the important part: This is functionally identical to the BlogController you posted: The state is generated internally on the fly every time output() is called and passed around internally, it never truely exists beyond the life of a call to $date->output(). This class can only ever output the current date. Compare this to your BlogController and at this basic level it’s identical: The class is self-configuring and has no real state.
Again, this is a very important distinction. If you cannot understand this point, then it’s not worth continuing this discussion at all.
[/b]
Hopefully, this is a clear enough example. Now, this is procedural because it is identical to:
function getCurrentDate() {
return time();
}
function format($date, $format) {
return date($format, $date);
}
function output() {
return format(getCurrentDate(), 'd/m/Y');
}
The problem this causes is quite large. Going back to the non-oo date class: How can I substitute the getCurrentDate() function?
class Date {
private function getCurrentDate() {
return time();
}
private function format($date, $format) {
return date($format, $date);
}
public function output() {
return $this->format($this->getCurrentDate(), 'd/m/Y');
}
}
Because it’s calling $this->format() internally it will ALWAYS call Date::getCurrentDate(). $this will always resolve to a Date object.
The only way this can be achieved is inheritance. I don’t want to change the code because it’s in use elsewhere and needs its current implementation.
So…
class TomorrowsDate extends Date {
private function getCurrentDate() {
return strtotime('+1 day');
}
}
This works, doesn’t it?
What if I want to change the date format in the output function for a specific instance of TomorrowsDate (Not all of them, mind!), I have to extend, again:
class TomorrowsDateAsMySql extends TomorrowsDate {
public function output() {
return $this->format($this->getCurrentDate(), 'Y-M-D');
}
}
But what if I want a normal date class formatted as MySQL… now I need to extend the base Date class again!
class DateAsMysql extends Date {
public function output() {
return $this->format($this->getCurrentDate(), 'Y-M-D');
}
}
And there we have repeated code! And a very messy, hard to understand class inheritance hierarchy.
The problem with this, and your controller examples is that the objects have no real state. They’re self-configuring and will only ever get configured in a very specific way. Their transient state only exists for the duration of one method and not beyond that, and because the state is dictated internally, it makes it impossible to substitute parts of the system.
Now imagine if we were’t using $this in the non-oo Date class but intead calling a method on another class and the pieces should fit together.
I hope that’s simple enough. I really can’t make it any simpler. It really is a case of OOP vs procedural.