Switching Classes during __construct() - php

Let's take the following classes for a moment:
class member {
public $id;
public function __construct($id) {
$this->id = $id;
// Check user level
if ($this->check_user_level() == moderator) {
$this = new moderator($this->id);
}
}
private function check_user_level() {
// Return user level from system based on user ID
}
}
class moderator extends member {
public function show_moderator_tools() {
// etc.
}
}
==================
$user = new member($user_id);
The desired behavior is to have $user detect whether the user is authorized to have moderator access, and if so recast the user using the moderator class instead of member.
PHP forbids simply reassigning $this, so what appears to be the best solution would be one of the following:
Run check_user_level($id) as a regular function and using an if statement
$user_level = check_user_level($id);
if ($user_level == "moderator") {
$user = new moderator($id);
} else {
$user = new member($id);
}
Set a flag in the base class that can be used for a check/redefinition after initializing $user
$user = new member($id);
if ($user->flag = TRUE) {
$user = new moderator($id);
}
The latter feels like it's introducing a security flaw, especially since the flag (which could just as easily be $user->user_level, or similar, I guess) would have to be public to be able to check it afterward.
What I would like to do would be to just make one call to new member($id) and have it handle things automatically from there, without the need for if statements after the fact. Is there a way to do this?

You can of do this by introducing another class (lets call it user) and using the __call magic method in php and call_user_func_array for calling the methods.
The logic is something like this -
Create a user class that has no method except check_user_level. It checks proper details and assigns it's $obj to the instance of either member or moderator class.
Here is how the classes would look like (I've changed the functions to print something out)-
class user{
public function __construct($id) {
$this->id = $id;
if ($this->check_user_level() == "moderator") {
$this->obj = new moderator($this->id);
}else{
$this->obj = new member($this->id);
}
}
public function __call($method, $args){
call_user_func_array(array($this->obj,$method), $args);
}
public function __get($prop){
if(isset($this->obj->$prop)){
return $this->obj->$prop;
}
return NULL;
}
private function check_user_level() {
// Return user level from system based on user ID
if($this->id == 1){
return "moderator";
}
return "member";
}
}
class member {
public $id;
public function __construct($id) {
$this->id = $id;
}
public function show_message($arg){
var_dump($this->id.$arg);
}
}
class moderator extends member{
public function show_moderator_tools() {
var_dump($this->id ."My toolset!");
}
}
So, now you can simply call the user class and that will automatically decide if it's a member or a moderator and call the method if it exists.
//Trying out a few examples
//Creating a member user
$mem_obj = new user(213);
$mem_obj->show_message("New message");
//Creating a moderator user
$mod_obj = new user(1);
$mod_obj->show_moderator_tools();
/*
OUTPUTS
-------
string(14) "213New message"
string(12) "1My toolset!"
*/
But you need to be careful with these kind of hacks.
For instance -
//This will fail
//because mem_obj doesn't have show_moderator_tools()
$mem_obj->show_moderator_tools();
EDIT
You can similarly go ahead with redirecting to properties using __get.
I have modified the code above to add this method beneath __call.
//Testing it
var_dump($mem_obj->id);
//This is an illegal property
var_dump($mem_obj->illegelProperty);
/*
OUTPUTS
int(213)
NULL
*/

Related

How to add functionality to an object in PHP

I am creating a web site that essentially sells advertising 'spots'. I.e someone can signup and buy a banner advert to be displayed on the home page, or they can buy an advert where they get their own profile page. My point being, although all adverts share common functionality, they do differ.
To accomplish this, my domain model looks like this: (simplified)
class Advert {
protected
$uID,
$startTime,
$traits = array();
public function __construct($_traits) {
$this->traits = $_traits;
}
public function getUID() { return $this->startTime; }
public function getStartTime() { return $this->startTime; }
public function setStartTime($_startTime) { $this->startTime = $_startTime; }
public function save() {
MySQLQuery 'UPDATE adverts SET startTime = $this->startTime WHERE uID = $this->uID';
foreach($this->traits as $trait) {
$trait->save($this->uID);
}
}
....
}
-
interface IAdvertTrait {
public function save($_advertUID);
}
-
class AdvertTraitProfile implements IAdvertTrait {
protected $url;
public function getURL() { return $this->url; }
public function setURL($_url) { $this->url = $_url; }
public function save($_advertUID) {
MySQLQuery 'UPDATE advertdata_profile SET url = $this->url WHERE advertUID = $_advertUID';
}
....
}
-
class AdvertTraitImage implements IAdvertTrait {
protected $image;
public function getImage() { return $this->image; }
public function setImage($_image) { $this->image = $_image; }
public function save($_advertUID) {
MySQLQuery 'UPDATE advertdata_image SET image = $this->image WHERE advertUID = $_advertUID';
}
....
}
There are actually several 'AdvertTrait...' classes, all of which implement IAdvertTrait.
As you can see, if I create an advert like this:
$advert = new Advert(
array(
new AdvertTraitProfile(),
new AdvertTraitImage()
...
)
);
I can then do this:
$advert->save();
And all the required information will get saved to the DB by the Advert itself and each of its AdvertTraits.
Using this method I'm able to create different kinds of advert simply by passing in different 'traits'. However, to my problem - I've no idea how I should go about manipulating an Advert. As per the example above, there is really no point creating and advert and then immediately saving it.
I'd like to be able to this:
$advert->getStartTime(); # Works
$advert->getURL(); # Doesn't work of course, as the getURL method is encapsulated within a property of the Advert's 'traits' array
$advert->setImage('blah.jpg'); # Also does not work
I'm not sure how to go about making these 'internal' methods accessible.
I could just create a different 'Advert' class for each kind of advert i.e:
AdvertProfile extends Advert {
$this->traitProfile = new AdvertTraitProfile();
public function getURL() { return $this->traitProfile->getURL(); }
...
}
AdvertImage extends Advert {
$this->traitImage = new AdvertTraitImage();
public function getImage() { return $this->traitImage->getImage(); }
...
}
AdvertProfileImage extends Advert {
$this->traitProfile = new AdvertTraitProfile();
$this->traitImage = new AdvertTraitImage();
public function getURL() { return $this->traitProfile->getURL(); }
public function getImage() { return $this->traitImage->getImage(); }
...
}
But I feel this is going to get messy; I'd need to keep creating new 'Advert' classes for every combination of traits I need and each advert class would need to define its trait methods in itself so they can be called from an instance of the advert.
I've also messed with the decorator pattern; so instead of passing these 'trait' classes to the constructor of the Advert, I chain the decorators together like:
$advert = new AdvertImageDecorator(new AdvertProfileDecorator(new Advert()));
However this requires the decorators to be able to 'lookup' methods that don't belong to them using method_exists and call_user_func_array which just seems like a big old hack to me. Plus chaining a multitude of decorators together like that just grates on me.
I've also had a look at proper PHP Traits, but IMVHO I do not think they'll help me. For example, every AdvertTrait has a 'save' method, all of which need to be called at the same time. I believe a proper Trait would require me to pick just one 'save' method from one trait.
Maybe I should use plain old inheritance - but then I'd still be creating specific types of Advert, all of which ultimately inherit from Advert. However I believe this would cause further issues; i.e I would not be able to make a AdvertWithProfileAndImageTraits extend from both AdvertWithProfileTraits AND AdvertWithImageTraits.
Can anyone offer a proper solution to this conundrum? Perhaps there is another design pattern I should be using.
Thanks very much,
Dave
I would go for the Decorator approach.
An abstract AdvertDecorator class can look like this:
abstract class AdvertDecorator implements IAdvertTrait {
protected $child;
public function __construct($child=null) {
if(!$child) {
$child = new NullAdvert();
}
$this->child = $child;
}
/**
* With this function all calls to non existing methods gets catched
* and called on the child
*/
public function __call($name, $args) {
return call_user_func_array(array($this->child, $name), $args);
}
}
/**
* This class is for convenience so that every decorator
* don't have to check if there is a child
*/
class NullAdvert implements IAdvertTrait {
public function save($_advertUID) {
// do nothing
}
}
Instead of the NullAdvert class you can use a BaseAdvert class, which implements all of your basic advert logic (like you have done in the Advert class).
Now all other classes extend from this AdvertDecorator class:
class AdvertProfile extends AdvertDecorator {
public function getProfileURL() { ... }
public function save($_advertUID) {
// save own advert
MySQLQuery 'UPDATE advertdata_profile SET url = $this->url WHERE advertUID = $_advertUID';
// save advert of child
$this->child->save($_advertUID);
}
}
class AdvertImage extends AdvertDecorator {
public function getImage() { ... }
public function save($_advertUID) {
// save own advert
MySQLQuery 'UPDATE advertdata_image SET image = $this->image WHERE advertUID = $_advertUID';
// save advert of child
$this->child->save($_advertUID);
}
}
class AdvertProfileImage extends AdvertDecorator {
public function getProfileImageURL() { ... }
public function getProfileImage() { ... }
public function save($_advertUID) {
// save own advert ...
// save advert of child
$this->child->save($_advertUID);
}
}
You can use it like this:
$advert = new AdvertProfile();
$advert = new AdvertImage($advert);
$advert = new AdvertProfileImage($advert);
// save all advert components
$advert->save('uid');
// call functions
$advert->getProfileURL();
$advert->getImage();
$advert->getProfileImageURL();
$advert->getProfileImage();
This structure is IMHO very flexible. Every Advert Component can be added to the current Advert in arbitrary order. Futhermore you can extend this solution with the composite pattern and add a AdvertComposite so that you can group your components. You can even add multiple Advert Components of the same kind to one Advert (for this you have to change the methods a little bit).

Is there a way to make __get & __set trigger for defined but empty properties in class

I´ve tried to understand if there is a magic function in php that can trigger a function when I try to access an object property like the following.
class User {
public $groups;
function loadGroups(){
$this->groups[] = "a list of groups";
}
function __get($name){
// Trigger on defined property such as groups
if($name == "groups"){
$this->loadGroups();
return $this->groups;
}
}
}
$user = new User();
foreach($user->groups as $group) // Is it possible to load $user->groups when it's accessed?
echo $group;
I do know that the __get & __set does not trigger if the property is defined in the class even tho the property is set to null or undefined, but is there any other way to trigger something for a defined property or do I have to create getters and setters for all these properties and make sure to always call these when i need to access the property and want to be sure that it's loaded when i access it? I do hope not due to It will result in many changes for the current system I'm working with.
I'm thankful for any information or help I can get to close this chapter.
// ZarToK
I get what you're trying to do, but I don't think you're asking the question right. You're obviously trying to do lazy loading.
There isn't a magic method for doing what you're requesting, but you can essentially create such a thing. There are two options: the "creating getters/setters" option - which is more in line with what a Java developer would do, or there's the "protect the variable and use magic method" option which tends to be "nice" for framework apis but can be a little confusing.
class User {
protected $groups;
private function loadGroups() {
$this->groups[] = 'a list of groups';
}
public function __get($name) {
if ($name == 'groups' && is_null($this->groups)) {
$this->loadGroups();
return $this->groups;
}
}
}
alternatively, the more Java way:
class User {
protected $groups;
private function loadGroups() {
$this->groups[] = 'a list of groups';
}
public function getGroups() {
if (is_null($this->groups)) {
$this->loadGroups();
}
return $this->groups;
}
}
__get() and __set() are used for reading/writing data from/to inaccessible properties. Inaccessible properties in this case would be private or protected, never public. If they need to be public then what is wrong with just returning from the function?
class User {
public $groups;
function loadGroups(){
$this->groups[] = "a list of groups";
return $this->groups;
}
}
$user = new User();
foreach($user->loadGroups() as $group)
echo $group;

SQL query efficiency when using PHP classes

I've been programming in PHP for many years, however only recently started programming with classes. I have the following - basic - user class as follows:
<?php
/* The class for constructing any user's information
*/
class User {
protected $userId, $email, $userGroup;
protected function getEmail() {
return $this->email;
}
protected function getUserId() {
return $this->userId;
}
protected function getUserGroup() {
return $this->userId;
}
public function __construct($userId='') {
if($userId) {
$select = mysql_query("SELECT userId, email, user_group FROM user WHERE userId = '$userId'");
while($user==mysql_fetch_array($select)) {
$this->email = $user[email];
$this->userId = $userId;
$this->userGroup = $user[user_group];
}
}
}
}?>
So I understand I can do the following
<?php
$user = new User($userId);
echo $user->getEmail();
?>
To display the user's email address for a given userId. What I'd like to know is, what would be the best way - using OOP - to display, say, 40 user's emails. Obviously creating 40 user objects would be silly as that's 40 SQL queries. Would you simply make a "users" class that was used for returning an array of multiple users, after doing an SQL given various parameters?
Ie
<?php
$getUsers = new Users('Tom');
// create 'Users' object where name is 'Tom'
print_r($users);
// prints the returned array of users?
?>
Thanks for your help. Hopefully this is clear.
I'd do it something like this (using another class):
class UserRepository {
public function getByName($name) {
$result = mysql_query("SELECT userId, email, user_group FROM user WHERE name = '$name'");
$users = [];
while ($row = mysql_fetch_assoc($result)) {
$user = new User;
$user->email = $row['email'];
$user->userId = $row['userId'];
$user->userGroup = $row['user_group'];
$users[] = $user;
}
return $users;
}
}
Addition:
The following example gives a good idea on how you can make the classes more testable and easy to modify in the future should they need to be:
UserRepositoryInterface
interface UserRepositoryInterface {
public function getByName($name);
public function getByUserId($id);
}
MySqliUserRepository
class MySqliUserRepository implements UserRepositoryInterface {
public function getByName($name) {
// Get by name using mysqli methods here
}
public function getByUserId($id) {
// Get by user id using mysqli methods here
}
}
PDOUserRepository
class PDOUserRepository implements UserRepositoryInterface {
public function getByName($name) {
// Get by name using PDO methods here
}
public function getByUserId($id) {
// Get by user id using PDO methods here
}
}
Usage
class Foo {
protected $userRepository;
public function __construct(UserRepositoryInterface $userRepository) {
$this->userRepository = $userRepository;
}
public function bar() {
$user = $this->userRepository->getByUserId(10);
}
}
Regarding use of mysql_
It may not be exactly how you do it but it'll give you an idea. Also mysql_ is depreciated so its best to use mysqli_ or PDO (my personal recommendation). PDO is also much more OOP friendly.
PDO: http://php.net/manual/en/book.pdo.php
mysqli_: http://php.net/manual/en/book.mysqli.php
Update:
Your individual user class would simply contain information relating to the user. The user class shouldn't contain any way to retrieve a user, that is the job of the repository. So if you want to retrieve 1 user, instead of doing in the User __construct as you currently do, add a method to the UserRepository that looks something like this:
public function getByUserId($id) {
// Select user from db, check only 1 exists, make user object, return.
}
I try to separate my data objects from the DB stuff. In your case, I'd make the following arrangements:
An instance of the User class represents an individual user, either in DB or not. The constructor does not retrieve anything from DB, it just populates class properties.
For users not in DB (e.g., a newly created user) the userId property is NULL (not '').
Methods that do DB stuff expect a database interface (or at least an object) as argument:
public function save(PDO $pdo){
}
There're static methods to fetch stuff from DB where a class instance does not make sense yet; they return either a User instance or a User collection:
public static function fetchById(PDO $pdo, $id){
}
public static function fetchAll(PDO $pdo){
}
When it makes sense, I write a private method to share common code:
private static function fetch(PDO $pdo, array $filter=array()){
$sql = 'SELECT id, email, group
FROM user' . PHP_EOL;
$params = array();
if( isset($filter['id']) ){
$sql .= 'WHERE id=:id';
$params['id'] = $filter['id'];
}
//...
}
public static function fetchById(PDO $pdo, $id){
$data = self::fetch($pdo, array('id' => $id));
if( empty($data) ){
return NULL;
}else{
reset($data);
return curren($data);
}
}
public static function fetchAll(PDO $pdo){
return self::fetch($pdo);
}
The User collection I've mentioned can as simple as an array of User instances, or as elaborate as your own generics implementation.
This way, a typical script looks like this:
// Fetch my details
$me = User::fetchById(1);
// Create a new user
$you = new User(NULL, 'Joe', 'Guests');
$you->save($pdo);
$message = sprintf('User created with ID=%d', $you->userId);
Instead of using the construct to retrieve a user, you could use methods to insert/retrieve users instead.
You can create methods to insert new users, update a particular user, retrieve one particular user or retrieve all (with conditions) in an array of users' objects, etc

Injecting single-use object into class

I have the following code:
<?php
class X
{
public function do($url)
{
$httpRequest = new \HttpRequest\Curl($url, $this->getOptions());
$httpRequest->fire();
// etc.
}
// ...
}
In order to be able to unit test this class, I'd like to inject a mocked HttpRequest class. One way to do this would be as follows:
<?php
class X
{
private $httpRequestClass;
public function __construct($httpRequestClass = '\HttpRequest\Curl')
{
$this->httpRequestClass = $httpRequestClass;
}
public function do($url)
{
$httpRequest = new $this->httpRequestClass($url, $this->getOptions());
$httpRequest->fire();
// etc.
}
// ...
}
But this doesn't seem right. Any other ideas?
public function __construct($url, $httpRequestClass = null)
{
$this->url = $url;
if ($httpRequestClass == null) //> Default
$this->httpRequestClass = new HttpRequest\Curl($this->url);
else
$this->httpRequestClass = $httpRequestClass;
}
so when you are using this class normally just call it with one param
yourClass('your url');
Otherwise pass the istance in the second argument
yourClass('url', new MockedObj);
Of course you should always Inject your dependencies without providing a default object
The class needs to generate objects of type HttpRequest, but we don't necessarily want it to initialize an object: we may want it to use the prototype pattern, for example. Therefore, the class calls for the factory pattern. I chose a factory callback, as opposed to a factory class, for brevity.
<?php
class X
{
private $factoryCallback;
public function __construct($factoryCallback = null)
{
$this->factoryCallback = $factoryCallback;
}
public function do($url)
{
$httpRequest = $this->createHttpRequest($url);
$httpRequest->fire();
// etc.
}
private function createHttpRequest($url)
{
$callback = $this->factoryCallback;
if (is_callable($callback)) {
return $callback($url, $this->getOptions());
}
return new \HttpRequest\Curl($url, $this->getOptions());
}
// ...
}
The helper method, createHttpRequest(), is a bit redundant in this example, but would be used for error handling in production code.

Organization of work with DataBase

I have some expierence in PHP, but have no one in application architecture
Now I want to orginize my own "bicycle". It's something not useful, maybe mini-framework or mini-application, I want get some exp here.
I need now to write classes for work with database and classese for entities (one of them isUser)
I have following code for database (some cheks and method are omitted to minify this question):
namespace DataBase;
class DataBase {
/**
*
* #var \PDO $pdo
*/
public $pdo;
public function __construct($host, $dbname, $username, $password=''){
$this->pdo = new \PDO('mysql:host='.$host.';dbname='.$dbname, $username, $password,
array(\PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'UTF8'"));
$this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
}
/**
*
* #param string $statement
* #return Statement
*/
public function prepare($statement){
return new Statement($this->pdo->prepare($statement));
}
}
namespace DataBase;
class Statement {
private $stmt;
public function __construct(\PDOStatement $stmt) {
$this->stmt = $stmt;
}
public function query() {
try {
$this->stmt->execute();
return $this; //for chaining
}
public function bind($key, $value) {
$this->stmt->bindValue($key, $value, $this->typeof($value));
return $this; //for chaining
}
//some methods for fetching data(works with fetch,fetchAll, fetchColumn and different PDO::FETCH_ methods
public function fetchUpdate($obj) {
$this->stmt->setFetchMode(\PDO::FETCH_INTO, $obj);
$this->stmt->fetch();
}
public function fetchRow() {
return $this->stmt->fetch(\PDO::FETCH_OBJ);
}
public function fetchRowClass($class) {
return $this->stmt->fetchObject($class);
}
}
And Some dummy for User class
<?php
/**
* Description of User
*
* #author riad
*/
class User {
private $id;
private $name = null;
private $status = null;
private $hasInfo = false;
private static $cache=array();
public function __construct() {
}
public function getId() {
return $this->id;
}
public function getName() {
if(!$this->hasInfo)
$this->getInfo ();
return $this->name;
}
public function isAuthorized(){
return $this->status!="noauth";
}
public static function createById($id) {
// I want this method to cerate user from id, then get info only I will use it after that
if(array_key_exists($id,self::$cache)){
return self::$cache[$id];
}
$user = new User;
$user->id = $id;
return $user;
}
private function getInfo(){
try{
\FrontController::getInstance()->getDB()->
prepare('SELECT * FROM `users` WHERE `id`=:id')->
bind('id', $this->id)->query()->fetchUpdate($this);
$this->hasInfo = true;
}
catch(\DataBase\NotFoundException $dbe){
$this->status = "deleted";
}
}
public static function createFromRequest($request){
$user = new User;
try{
//try get data from cookie
\FrontController::getInstance()->getDB()->
prepare('SELECT * FROM `users` WHERE `session` = :session AND `id`= :id')->
bind('id', $id)->bind('session',$session)->query()->
fetchUpdate($user);
}
catch(... $e){
$user->status = "noauth";
$user->id = 0;
// make it unregged
}
return $user;
}
}
?>
I have some problems with it.
I don't want set properties from database, that are not listed in props of class list(is not so important, of course). I know that I can use public function __call($name,$value){
//do nothing;
}
I want to mkae this props private, but want also use $stmt->fetchUpdate($obj) I know I can use public function __call($name,$value){
$this->$name=$value;
}, but it's as declare props public and it is on the road with first point
I can also use public function __call($name,$value){
if($name=='id'){
$this->id=$value;
}
else if($name=='status'){
$this->status=$value;
}
} But it's not comfortable to write it for every entity class and not save as from publicity of this methods
I want to set $this->hasInfo to true when I get this class from database. I know I can change my Database class to always set some variable to true when by default it's false. But it seems to be not elegant.
I want to update cache when I set id (It maybe used as previos point)
Is it possible to avoid fetchRowClass write direct to props and use setter as with fetchUpdate? Or maybe allow fetchUpdate direct access?
I know I write a lot of code self but I want your opinion:
What should I improve?
What are other/the best possible solution for problems from previos list?
Hope, It's not so hard to read and understand.
Glad to see any suggestions
With regards Alex
Few tips: [based on my own experience and frameworks i already used]
Basically what you should/want/might do is to create a SuperClass for all the clases in you model. This class will contain the reference to the Database Instance, and it will have all the common methods for your model, i.e. getById($id), getAll(),getPaginated(), etc.
The other goal of this SuperClass is to map the results from the database into Instances of your Model's Classes. So in the end, your user class would have only properties, accessors and methods that are specific to the class, like special queries or something like that.
Here's an example of what this could look like:
Class Model{
protected function getById($_id){
$_table = get_class($this);
$anonymous = $this->_getObjectById($_table,$_id); //this method will execute a query (unsing PDO) and return a StdClass object with the results
$this->mapTable($anonymous,$_table); //this method will take the StdClass instance and will transform it into a $_table Instance
}
private function mapTable($stdInstance,$model_name){
foreach($stdInstance as $key => $value){
try{
if(property_exists($this,$key)){
$this->$key = $value; //you could declare the model's properties as protected... or you could create accessors and call them here
}
} catch(Exception $ex) {
/* something went wrong o_O */
}
}
Class User extends Model{
protected $id;
protected $name;
.....
}
Class Controller{
public function index(){
$user = new User();
$user->getById($_GET['id']);
print_r($user);
//now you can pass the $user object to the View to display it
}
}
in a few words... the Model class is a very small ORM. You could try to create your own ORM, (like i did) but you'll face a lot of problems when trying to map the relations between objects: Nx1,1xN,NxN,1x1, inheritance, "deeper relations" and the n+1 problem. You'll also need to, somehow, define the model structure so your ORM could understand it, maybe using YAML/XML files or reading the structure directly from the table structure of your databe, or having a naming convention in your properties...
its a really interesting field :)
Hope this helps and Good Luck

Categories