Jump to content

Recommended Posts

Hey,

 

I am trying to creating a database object that does all my database findings, and use it as a sub class for all my other classes that will be needing to use it.  I have created a sub_class called database_object.php which will be my parent class.

 

I am trying to create a method in the database_object.php that can see if the property exists in any master class that I attach it to., I am having trouble finding a way to do that. I have a way to do this if the method was inside the the master class , but not when I am extending the master class with my sub_class.  This is how I currently have it with the method inside the master class.

 

Any suggestion on how to use the property_exists from the parent class?

protected function attributes() {
	$attributes = array();

	foreach(static::$db_fields as $field) {
		if(property_exists($this, $field)) {
			$attributes[$field] = $this->$field;
		}
	}
	return $attributes;
}

Magic: bad. Proper (SOLID+): good. And yes, that's simplified to a nauseating level.

 

As a sidenote, method names should be commands. Possibly you learned JavaScript first, JS frameworks have a nasty habit of sacrificing semantics in favor of a few key presses. With the exception of more elaborate frameworks such as ExtJS.

Magic methods are that bad? What's wrong with an implementation like

/**
 * @property-read int $id
 * @property string $name
 * @property int $size
 */
class Photograph extends DatabaseObject {

	// ...

}

abstract class DatabaseObject {

	private $data = array();

	public function __get($name) {
		if (isset($this->data[$name])) {
			return $this->data[$name];
		} else {
			// error
		}
	}

	public function __set($name, $value) {
		if (isset($this->data[$name])) {
			// check accessibility/validation/etc and set
		} else {
			// error
		}
	}

}

Any kind of magic in your app is bad, I think everyone agrees on that and when you can avoid it, you should. Like overloading operators in C++ and Ruby. At some point it bites you. And when you really need to, you should properly document it (@property, @method).

 

To use your example, in this particular case (which is a pretty bad use of __set and __get yet ever so popular) you would end up with a lot of code for each supported property inside __set and __get (possibly making each method extend beyond what you can fit on your screen, which should be a clear indication something is wrong) and each time your class would need more properties you would have to dig into that already messy code and extend it and hope you don't break anything in the process.

 

Basically you end up emulating what OO provides out of the box. Also __set and __get are PHP4 artifacts which are since PHP5 replaced by proper interfaces ArrayAccess and Serialize for __sleep and __wakeup.

Edited by ignace

Besides there is no magic necessary:

 

class Photograph extends SplFileInfo implements ImageInterface {
  private $width;
  private $height;
  private $mimeType;
  
  public function __construct($filename) {
    list($this->width, $this->height) = $image = getimagesize($filename);
    $this->mimeType = $image['mime'];
    
    parent::__construct($filename);
  }
  
  public function getWidth() {}
  public function getHeight() {}
  public function getMimeType() {}
}

interface ImageResizeStrategy {
  public function resize(ImageInterface $image);
}

class FixedResize implements ImageResizeStrategy {
  ..
  
  public function resize(ImageInterface $image) {
    ..
  }
}

class ProportionalResize implements ImageResizeStrategy {
  ..
  
  public function resize(ImageInterface $image) {
    ..
  }
}
Edited by ignace

Not to push the issue too far off topic but

Besides there is no magic necessary:

By using getters and setters, that's true. But I absolutely hate having them if only for the sake of having them - whether it's a property or a method, it's just syntax. (As I've heard it, the concept of getters and setters originated with code generation tools and I hate those too.) Meanwhile if their implementations are beyond simply getting and setting (ie, requires some amount of special logic) then that means there's underlying issues with how the data can or should be used and they require more complicated functionality beyond "change $X to Y".

 

Take width, height, and MIME type. As far as I'm concerned outside code should be able to read those three without having to jump through hoops. It's not sensitive data. But setting any of them by themselves should not be possible because (eg) a] the image is immutable, or b] doing so is not merely a matter of setting a new value since it involves resizing (width/height) or converting the image (MIME type). And both of those are actions performed on the entity as a whole, not quick changes to some attributes. On the other hand an image caption may be arbitrary so simply getting and setting should suffice.

 

I'm also implying, or trying to imply, that this all requires an intelligent setup (that, honestly, I'm very used to using) with concerns about visibility, immutability, validity, and issues like that. If you can't have that then you're precluding this whole idea.

I agree entirely with what you said, and it's also what I meant in my previous post that an "intelligent setup", as you put it, is the goal. But you answered your own question then, unless I missed your argument for __set/__get in your previous post?

Edited by ignace

(As I've heard it, the concept of getters and setters originated with code generation tools and I hate those too.)

 

Any form of boilerplate is a PITA. But without a defined interface you can forget about polymorphism. Your client code becomes coupled to some unknown runtime factor. Concretely, how does hour client code know wheter or not it can invoke DataBaseObject::getFoo()? Granted, you could employ a variant of duck-typing by enclosing calls in a try/catch block catching a BadMethodCallException, or add method to  DataBaseObject

 that allows clients to check the existence of a piece of data before it calls a getter, but then you are moving your boilerplate from the class to its clients, doesn't solve anything and the boilerplate in the class actually provides better encapsulation than scattering your application with unneeded error handling or state assertions.

 

Hope that makes sense.

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.