Object Oriented Programming Problem - php

I'm designing a little CMS using PHP whilst putting OOP into practice. I've hit a problem though.
I have a page class, whos constructor accepts a UID and a slug. This is then used to set the properties (unless the page don't exist in which case it would fail). Anyway, I wanted to implement a function to create a page, and I thought ... what's the best way to do this without overloading the constructor. What would the correct way, or more conventional method, of doing this be?
My code is below:
<?php
class Page {
private $dbc;
private $title;
private $description;
private $image;
private $tags;
private $owner;
private $timestamp;
private $views;
public function __construct($uid, $slug) {
}
public function getTitle() {
return $this->title;
}
public function getDescription() {
if($this->description != NULL) {
return $this->description;
} else {
return false;
}
}
public function getImage() {
if($this->image != NULL) {
return $this->image;
} else {
return false;
}
}
public function getTags() {
if($this->tags != NULL) {
return $this->tags;
} else {
return false;
}
}
public function getOwner() {
return $this->owner;
}
public function getTimestamp() {
return $this->timestamp;
}
public function getViews() {
return $this->views;
}
public function createPage() {
// Stuck?
}
}

PHP Doesn't support overloading. I would probably go for something like:
$oPage = new Page();
$oPage->setTitle($sSomeTitle);
$oPage->setcontent($sSomeContent);
$oPage->save();
Or you could create an extra object - that is responsible for handling this.
$oPage = new Page();
$oPage->setTitle($sSomeTitle);
$oPage->setcontent($sSomeContent);
PageManager::create($oPage);
The advantage of this - is that your page class remains a pure data container, where all logic for loading/saving is done within an separate class.

I put those arguments in a constructor without which an object cannot exist. Other (optional) attributes of an object can be set by calling its setters.
For example, in your case a page seems to always have a UID, without which your system cannot recognize it, so it should be an argument to the constructor and I would build it such that the callee of the constructor shall not be able to call an empty constructor, because it won't mean anything for the page class.
A page can have no content and not even any title, but it has no meaning for the system without a UID or for example a URI. So my callee's code would look as follows:
$page = new Page($uid);
$page->setTitle($sSomeTitle);
$page->setcontent($sSomeContent);
$page->render();
However, my discussion mainly pertains to modelling your class. I personally think that the second solution in Wesley's answer is better.

You can make static method, that returns Page object
public static function CreatePageWithExtraAttribute($uid, $slug, $extra) {
$page = new Page($uid,$slug);
//your code here
return $page;
}
Or make a static method that querys database for example
public static function CreatePage($extra) {
list($uid,$slug) = SomeDatabaseManager::GetUidSlug();
$page = new Page($uid,$slug);
//your code here
return $page;
}
Then use it
$page=Page::CreatePageWithExtraAttribute($title)

Related

How to return function values when using method chaining in PHP?

Say I have the following class:
class Test
{
private static $instance = false;
public static function test()
{
if(!self::$instance)
{
self::$instance = new self();
}
return self::$instance;
}
public function test1()
{
//...
}
public function test2()
{
//...
}
}
And I go about calling functions by chaining them like so:
$data = Test::test(...)->test1(...)->test2(...);
At the moment for the above method chain to work I have to keep returning $instance and I really would like it if I could return something from test2() to then be assigned to $data but I am not sure how to do this as I have to keep returning $instance in order for mt method chain to work?
If you want to chain methods, you need to return the current instance from any method which has another call chained on after it. However, it's not necessary for the last call in the chain to do so. In this case that means you're free to return whatever you like from test2()
Just bear in mind, if you return something different from test2() you'll never be able to chain anything onto it in future. For example, $data = Test::test(...)->test2(...)->test1(...); wouldn't work.
Protip: it's worth documenting your code with some comments explaining which ones are and aren't chainable so you don't forget in future.
Generally speaking if you are doing method chaining, and I'm assuming each of the tests above return your data model in a different state, and I assume that you want some data from the model itself. I would do the following:
class Test
{
private static $model;
public function test1() {
//do something to model
return $this;
}
public function test1() {
//do something to model
return $this;
}
public function finish_process() {
//process results
return $this.model;
}
}
so essentially i can do the following now:
$results = Test::test1()->finish_process();
and
$results = Test::test1()->test2()->finish_process();
You can pass the $data by its reference and you can change it or assign any data into it.
// inside class
public function test2( &$data ) {
$data = 'it will work';
}
// outside class
$data = '';
Test::test(...)->test1(...)->test2($data);
check this http://php.net/manual/en/language.references.pass.php
return $this inside test1() and test2() methods.

return type of container from subclass

I have a PHP library which I don't want to edit, and implement to my code by extending/overriding some methods. But I'm stuck with chainability. For example:
class MomentPHP extends Moment {
public $uniqueSettings;
public function formatJS(){
return parent::format($this->uniqueSettings);
}
}
class Moment {
public function startOf(){
//some code
return $this;
}
}
I want to do this:
$momentphp = new MomentPHP();
$dateStart = $momentphp->startof('month')->formatJs();
And the way to do this is overriding all the methods in the child class inside MomentPHP to return itself.
Is there any other simple way to do this? like using _call or something?
Edit: Found one way to do this:
Remove the inheritance,
Create a instance variable of parent class,
use __call method to switch between classes.
Like this:
class MomentPHP {
private $instance = null;
public $uniqueSettings;
public function __construct(){
$this->instance = new Moment();
}
public function __call($method,$args){
if(in_array($method, get_class_methods($this))){
call_user_func(array($this,$method),$args);
else
call_user_func(array($this->instance,$method),$args);
return $this;
}
public function formatJS(){
return $this->instance->format($this->uniqueSettings);
}
}
class Moment {
public function startOf(){
//some code
return $this;
}
}
Is there any better way?
One proper way to do this is:
class MomentPHP {
private $instance = null;
public $uniqueSettings;
public function __construct(){
$this->instance = new Moment();
// settings etc.
}
public function __call($method,$args){
$result = NULL;
if(in_array($method, get_class_methods($this))){
$result = call_user_func(array($this,$method),$args);
else
$result = call_user_func(array($this->instance,$method),$args);
if($result instanceof Moment)
$this->instance = $result;
return $this;
}
public function format(){
return $this->instance->format($this->uniqueSettings);
}
}
Updating the instance from the method result is the key operation, and using $this instead of $this->instance allows you to use the extender class in every call. So you can override the function while using other methods in the parent class with chaining ability.

Overriding static methods in PHP

I have an abstract page class looking like this:
abstract class Page {
public static function display() {
self::displayHeader();
self::displayContent();
self::displayFooter();
}
public static function displayContent() {
print "<p>some content</p>";
}
public static function displayHeader() {
include_once(kContent . "HeaderContent.class.php");
HeaderContent::display();
}
public static function displayFooter() {
include_once(kContent . "FooterContent.class.php");
FooterContent::display();
}
};
I would like to subclass from this, and only override the displayContent method, so the header and footer is being displayed automatically, but still having the option to override the display method, for example for .js files.
Now I have another class, looking like this:
class FooPage extends Page {
public static function displayContent() {
print "<p>Foo page</p>";
};
Now, instead of calling the FooPage's displayContent method, it just calls the one from the superclass.
Why? What can I do?
EDIT
I'm running PHP 5.2.17
Ilija, PHP < 5.3 doesn't have "Late Static Binding" and that's why you may be experiencing the FooPage::displayContent not being called. If you are running PHP 5.2 then there is nothing much to do (except for some hacks using debug_backtrace(), which honestly I wouldn't recommend for this situation).
Now, what it really calls my attention is that your methods are all static; is there a reason for this? Why aren't they instance methods? I would expect something like:
include_once(kContent . "HeaderContent.class.php");
include_once(kContent . "HeaderContent.class.php");
abstract class Page
{
protected $header;
protected $footer;
public function __construct()
{
$this->header = new HeaderContent();
$this->footer = new FooterContent();
}
public function display()
{
$this->displayHeader();
$this->displayContent();
$this->displayFooter();
}
public function displayContent()
{
print "<p>some content</p>";
}
public function displayHeader()
{
$this->header->display();
}
public function displayFooter()
{
$this->footer->display();
}
};
class FooPage extends Page
{
public function displayContent()
{
print "<p>Foo page</p>";
}
}
and later in your view you would write something like:
$page = new FooPage();
$page->display();
Some things to take into account:
It is generally better not to use print/echo when generating a view content. Instead try to create the string and do the print/echo as a last step. This makes it easier to later write tests.
Example:
public function display()
{
return
$this->displayHeader() .
$this->displayContent() .
$this->displayFooter();
}
public function displayContent()
{
return "<p>some content</p>";
}
public function displayHeader()
{
return $this->header->display();
}
....
$page = new FooPage();
echo $page->display();
If you need to do it as your application grows, you can pass the header and footer as Page constructor parameters. As long as they are objects that understand the display() message (i.e. polymorphic) things should be ok.
HTH
Returned back to this question. Was looking for solution for Symfony (5.4).
And I finally came with this "Service - method call" solution.
#services_dev.yaml:
Company\Core\PinGenerator\PinGenerator:
calls:
- [setDebugMode, [true]]
#PinGenerator:
class PinGenerator implements PinGeneratorInterface
{
public static bool $inDebugMode = false;
public static function setDebugMode(bool $inDebugMode): void
{
self::$inDebugMode = $inDebugMode;
}
public static function generate(int $length = self::DEFAULT_PIN_CODE_LENGTH, bool $numbersOnly = true): string
{
if (!self::$inDebugMode) {
return PinGeneratorProd::generate($length, $numbersOnly);
} else {
return PinGeneratorDev::generate($length, $numbersOnly);
}
}
}
Honesly hoping, that this will help someone, someday.

Is it necessary to cache objects?

For a project I have some objects, one method is to load the parent entity.
So I call the method like this:
$this->getDetails()->getEntity();
So the code of getEntity is:
public function getEntity()
{
if (isset($this->entity)) {
return $this->entity;
} else {
$mapper = new Crm_Mapper_Entity();
return $this->entity = $mapper->find($this->customerId);
}
}
Is it necessary to load the entity into the attribute? Because when I want to load it on another place it shouldnt be calling the mapper again.
Or is it that when it's loaded, the object is already in memory and I don't need to put it into the attribute?
Thanks
use singleton pattern like this:
class MyStatic{
private static $instance;
private __construct(){}
public static getInstance(){
if (!empty(MyStatic::$instance;)) {
return MyStatic::$instance;
} else {
$mapper = new Crm_Mapper_Entity();
$thisMyStatic::$instance = $mapper->find($this->customerId);
return $thisMyStatic::$instance;
}
}
}

Faking method attributes in PHP?

Is it possible to use the equivalent for .NET method attributes in PHP, or in some way simulate these?
Context
We have an in-house URL routing class that we like a lot. The way it works today is that we first have to register all the routes with a central route manager, like so:
$oRouteManager->RegisterRoute('admin/test/', array('CAdmin', 'SomeMethod'));
$oRouteManager->RegisterRoute('admin/foo/', array('CAdmin', 'SomeOtherMethod'));
$oRouteManager->RegisterRoute('test/', array('CTest', 'SomeMethod'));
Whenever a route is encountered, the callback method (in the cases above they are static class methods) is called. However, this separates the route from the method, at least in code.
I am looking for some method to put the route closer to the method, as you could have done in C#:
<Route Path="admin/test/">
public static void SomeMethod() { /* implementation */ }
My options as I see them now, are either to create some sort of phpDoc extension that allows me to something like this:
/**
* #route admin/test/
*/
public static function SomeMethod() { /* implementation */ }
But that would require writing/reusing a parser for phpDoc, and will most likely be rather slow.
The other option would be to separate each route into it's own class, and have methods like the following:
class CAdminTest extends CRoute
{
public static function Invoke() { /* implementation */ }
public static function GetRoute() { return "admin/test/"; }
}
However, this would still require registering every single class, and there would be a great number of classes like this (not to mention the amount of extra code).
So what are my options here? What would be the best way to keep the route close to the method it invokes?
This is how I ended up solving this. The article provided by Kevin was a huge help. By using ReflectionClass and ReflectionMethod::getDocComment, I can walk through the phpDoc comments very easily. A small regular expression finds any #route, and is registered to the method.
Reflection is not that quick (in our case, about 2,5 times as slow as having hard-coded calls to RegiserRoute in a separate function), and since we have a lot of routes, we had to cache the finished list of routes in Memcached, so reflection is unnecessary on every page load. In total we ended up going from taking 7ms to register the routes to 1,7ms on average when cached (reflection on every page load used 18ms on average.
The code to do this, which can be overridden in a subclass if you need manual registration, is as follows:
public static function RegisterRoutes()
{
$sClass = get_called_class(); // unavailable in PHP < 5.3.0
$rflClass = new ReflectionClass($sClass);
foreach ($rflClass->getMethods() as $rflMethod)
{
$sComment = $rflMethod->getDocComment();
if (preg_match_all('%^\s*\*\s*#route\s+(?P<route>/?(?:[a-z0-9]+/?)+)\s*$%im', $sComment, $result, PREG_PATTERN_ORDER))
{
foreach ($result[1] as $sRoute)
{
$sMethod = $rflMethod->GetName();
$oRouteManager->RegisterRoute($sRoute, array($sClass, $sMethod));
}
}
}
}
Thanks to everyone for pointing me in the right direction, there were lots of good suggestions here! We went with this approach simply because it allows us to keep the route close to the code it invokes:
class CSomeRoutable extends CRoutable
{
/**
* #route /foo/bar
* #route /for/baz
*/
public static function SomeRoute($SomeUnsafeParameter)
{
// this is accessible through two different routes
echo (int)$SomeUnsafeParameter;
}
}
Using PHP 5.3, you could use closures or "Anonymous functions" to tie the code to the route.
For example:
<?php
class Router
{
protected $routes;
public function __construct(){
$this->routes = array();
}
public function RegisterRoute($route, $callback) {
$this->routes[$route] = $callback;
}
public function CallRoute($route)
{
if(array_key_exists($route, $this->routes)) {
$this->routes[$route]();
}
}
}
$router = new Router();
$router->RegisterRoute('admin/test/', function() {
echo "Somebody called the Admin Test thingie!";
});
$router->CallRoute('admin/test/');
// Outputs: Somebody called the Admin Test thingie!
?>
Here's a method which may suit your needs. Each class that contains routes must implement an interface and then later loop through all defined classes which implement that interface to collect a list of routes. The interface contains a single method which expects an array of UrlRoute objects to be returned. These are then registered using your existing URL routing class.
Edit: I was just thinking, the UrlRoute class should probably also contain a field for ClassName. Then $oRouteManager->RegisterRoute($urlRoute->route, array($className, $urlRoute->method)) could be simplified to $oRouteManager->RegisterRoute($urlRoute). However, this would require a change to your existing framework...
interface IUrlRoute
{
public static function GetRoutes();
}
class UrlRoute
{
var $route;
var $method;
public function __construct($route, $method)
{
$this->route = $route;
$this->method = $method;
}
}
class Page1 implements IUrlRoute
{
public static function GetRoutes()
{
return array(
new UrlRoute('page1/test/', 'test')
);
}
public function test()
{
}
}
class Page2 implements IUrlRoute
{
public static function GetRoutes()
{
return array(
new UrlRoute('page2/someroute/', 'test3'),
new UrlRoute('page2/anotherpage/', 'anotherpage')
);
}
public function test3()
{
}
public function anotherpage()
{
}
}
$classes = get_declared_classes();
foreach($classes as $className)
{
$c = new ReflectionClass($className);
if( $c->implementsInterface('IUrlRoute') )
{
$fnRoute = $c->getMethod('GetRoutes');
$listRoutes = $fnRoute->invoke(null);
foreach($listRoutes as $urlRoute)
{
$oRouteManager->RegisterRoute($urlRoute->route, array($className, $urlRoute->method));
}
}
}
I'd use a combination of interfaces and a singleton class to register routes on the fly.
I would use a convention of naming the router classes like FirstRouter, SecondRouter and so on. This would enable this to work:
foreach (get_declared_classes() as $class) {
if (preg_match('/Router$/',$class)) {
new $class;
}
}
That would register all declared classes with my router manager.
This is the code to call the route method
$rm = routemgr::getInstance()->route('test/test');
A router method would look like this
static public function testRoute() {
if (self::$register) {
return 'test/test'; // path
}
echo "testRoute\n";
}
The interfaces
interface getroutes {
public function getRoutes();
}
interface router extends getroutes {
public function route($path);
public function match($path);
}
interface routes {
public function getPath();
public function getMethod();
}
And this is my definition av a route
class route implements routes {
public function getPath() {
return $this->path;
}
public function setPath($path) {
$this->path = $path;
}
public function getMethod() {
return $this->method;
}
public function setMethod($class,$method) {
$this->method = array($class,$method);
return $this;
}
public function __construct($path,$method) {
$this->path = $path;
$this->method = $method;
}
}
The Router manager
class routemgr implements router {
private $routes;
static private $instance;
private function __construct() {
}
static public function getInstance() {
if (!(self::$instance instanceof routemgr)) {
self::$instance = new routemgr();
}
return self::$instance;
}
public function addRoute($object) {
$this->routes[] = $object;
}
public function route($path) {
foreach ($this->routes as $router) {
if ($router->match($path)) {
$router->route($path);
}
}
}
public function match($path) {
foreach ($this->routes as $router) {
if ($router->match($path)) {
return true;
}
}
}
public function getRoutes() {
foreach ($this->routes as $router) {
foreach ($router->getRoutes() as $route) {
$total[] = $route;
}
}
return $total;
}
}
And the self register super class
class selfregister implements router {
private $routes;
static protected $register = true;
public function getRoutes() {
return $this->routes;
}
public function __construct() {
self::$register = true;
foreach (get_class_methods(get_class($this)) as $name) {
if (preg_match('/Route$/',$name)) {
$path = call_user_method($name, $this);
if ($path) {
$this->routes[] = new route($path,array(get_class($this),$name));
}
}
}
self::$register = false;
routemgr::getInstance()->addRoute($this);
}
public function route($path) {
foreach ($this->routes as $route) {
if ($route->getPath() == $path) {
call_user_func($route->getMethod());
}
}
}
public function match($path) {
foreach ($this->routes as $route) {
if ($route->getPath() == $path) {
return true;
}
}
}
}
And finally the self registering router class
class aRouter extends selfregister {
static public function testRoute() {
if (self::$register) {
return 'test/test';
}
echo "testRoute\n";
}
static public function test2Route() {
if (self::$register) {
return 'test2/test';
}
echo "test2Route\n";
}
}
the closest you can put your path to the function definition (IMHO) is right before the class definition. so you would have
$oRouteManager->RegisterRoute('test/', array('CTest', 'SomeMethod'));
class CTest {
public static function SomeMethod() {}
}
and
$oRouteManager->RegisterRoute('admin/test/', array('CAdmin', 'SomeMethod'));
$oRouteManager->RegisterRoute('admin/foo/', array('CAdmin', 'SomeOtherMethod'));
class CAdmin {
public static function SomeMethod() {}
public static function SomeOtherMethod() {}
}
There is a proposal for this, it was declined. See the rfc here:
Attributes RFC at php.net
My solution to this desire is something like this:
abstract class MyAttributableBase{
protected static $_methodAttributes=[];
public static function getMethodAtributes(string $method):?array{
if( isset(self::$_methodAttributes[$method])){
return self::$_methodAttributes[$method];
}
return null;
}
protected static function setMethodAttributes(string $method, array $attrs):void{
self::$_methodAttributes[$method] = $attrs;
}
}
class MyController extends MyAttributableBase{
protected static function getMethodAtributes(string $method):?array{
switch( $method ){
case 'myAction':
return ['attrOne'=>'value1'];
default:
return parent::getMethodAttributes($method);
}
}
}
Usage:
$c = new MyController();
print $c::getMethodAttributes('myAction')['attrOne'];
You can of course use it from within a base class method to do "routing" stuff in this case, or from a routing class that operates on "MyAttributableBase" objects, or anywhere else you would want to inspect this attached metadata for any purpose. I prefer this "in-code" solution to using phpDoc. Note I didn't attempt to test this exact code but it is mentally copied from a working solution. If it doesn't compile for some small reason it should be easy to fix and use. I have not figured out a way to cleanly put the attributes near the method definition. Using this implementation in the base you COULD set the attributes within the method (myAction in this case) as the first code to execute, but it would not be a static attribute, it would get reset at each invocation. You could add code to additionally ensure it is only set once but that's just extra code to execute and maybe is not better. Overriding the get method allows you to set the info once and refer to it once, even though it's not that close to the method definition. Keeping the static array in the base does allow some flexibility if there are cases for adding or changing metadata at runtime. I could be possible to use something like phpDoc and a static constructor to parse that when the first class is created to populate the static metadata array. I haven't found a solution that is awesome but the one I'm using is adequate.

Categories