Skip to content

Instantly share code, notes, and snippets.

@mfields
Created June 3, 2011 16:21
Show Gist options
  • Save mfields/1006626 to your computer and use it in GitHub Desktop.
Save mfields/1006626 to your computer and use it in GitHub Desktop.
Making some Objects in PHP
<?php
/*
* Send all required arguments.
*/
$test = new My_Object( array(
'one' => 1,
'two' => 2,
'three' => 3,
) );
print '<pre>';
gettype( $test ); // Should be "My_Object"
var_dump( empty( $test ) ); // false as expected.
var_dump( $test );
print '</pre>';
/*
* This object is missing one required argument.
*/
$test = new My_Object( array(
'one' => 1,
'two' => 2,
) );
print '<pre>';
gettype( $test ); // Would be neat if this could return some sort of error object.
var_dump( empty( $test ) ); // Even though all properties have been removed, this still returns false?
var_dump( $test );
print '</pre>';
class My_Object {
public $one = null;
public $two = null;
public $three = null;
public function __construct( $args = array() ) {
$diff = array_diff_key( get_object_vars( $this ), $args );
if ( ! empty( $diff ) ) {
/* Let them know they're doing it wrong. */
trigger_error( 'Could not construct object.' );
/* It's possible to kill all of the properties. */
foreach( array_keys( get_object_vars( $this ) ) as $property ) {
unset( $this->$property );
}
/* Anyway to change type of object before return? */
return;
}
$this->one = (int) $args['one'];
$this->two = (int) $args['two'];
$this->three = (int) $args['three'];
}
}
@ericmann
Copy link

ericmann commented Jun 3, 2011

Hmm ... I wouldn't pass $args as an array. I'd build the constructor as:

public function __construct() {
    $args = func_get_args();
    ...
}

This allows you to instantiate the object a little more cleanly: $test = new My_Object( 1, 2, 3 );

Inside of __construct() you can test the size of $args to make sure they sent enough variables, then test the type of each element of $args to make sure they sent the right stuff. But every time you instantiate the object, whether construction fails or succeeds, gettype() will always return 'My_Object'

You really have three options:
1 - Set some property inside the object based on whether or not construction succeeded and check it later:

$test = new My_Object( 1, 2 );
if ( ! $test->it_works )
     ... do something ...

2 - Return false on failure:

if ( ! $test = new My_Object( 1, 2 ) )
    ... do something ...

3 - Throw an exception inside __construct() if the args are invalid. throw new InvalidArgumentException() works well here.

But changing the type of object before the return? I'm pretty sure you can't actually do that. PHP only has so many types available, and custom objects are all really just extensions of stdObject anyway. You could use settype() outside the object to cast it as something else, but nothing internal ...

@ericmann
Copy link

ericmann commented Jun 3, 2011

Actually, I did just find some reference to using settype( $this, 'null ); as a deconstructor for objects. You might be able to use a similar setup inside __construct() to re-cast your object as an error object, as null, as a Boolean, or whatever. But I can't test at the moment on this machine ...

@mfields
Copy link
Author

mfields commented Jun 3, 2011

Thanks for the feedback! I'm kind of in this named argument phase and I don't see it going away anytime soon quite honestly. I fear ever having to read documentation again just to know the purpose of the values that I'm passig to a function. Makes me angry :) ... or :( ... yeah, that's more like it.

I thought about doing #1. And this is what I will probably do in the end. Just thinking about alternative to doing this, but it really makes a lot of sense.
#2 really has no effect. Although I did not see it stated in the docs, I think that __constructor() returns void. Anything that I've tried along these lines has failed. I can never access the return value.
#3 This is pretty cool - I did not know that that function existed. Will definitely come in handy in the future! Unfortunately, it's a bit strict as it stops the script. Would much rather generate a notice and let the script continue.

settype inside the constructor did not seem to do anything ... started to get excited about this one, but no luck.
#1 is the winner so far!!!

I also had another idea and was wondering what you thought:

What if I were to create a boolean static method named check_args(). This could be called on the args before individual objects are created?

@ericmann
Copy link

ericmann commented Jun 3, 2011

What if I were to create a boolean static method named check_args(). This could be called on the args before individual objects are created?

Kind of like pre-validation? I think that would work ... but you'd need to document it well so others using the code would remember to use it before they instantiate their object.

if ( My_Object::check_args( $args ) ) {
    $test = new My_Object( $args );
} else {
    // die in fiery brillaince
}

@mfields
Copy link
Author

mfields commented Jun 3, 2011

Cool! Other people using the object is totally not permitted. My intention is to only use it internally in a plugin. Basically there would be a configuration array for "Icon Sets" that is filterable. This array would be looped over and a new Icon_Set object will be created only if it is configured properly.

@ericmann
Copy link

ericmann commented Jun 3, 2011

I meant "others using the code" as in anyone who forks your project or uses it as a base/inspiration for their own system.

Now that I know what you're trying to do (rather than mucking around with My_Objects and $tests), have you considered using a factory method?

Basically:

$test = create_my_object( $args );

function create_my_object( $args = array() ) {
    if ( Icon_Set::check_args( $args ) ) {
        return new Icon_Set( $args );
    } else {
        return new My_Error( "You're doing it wrong!" );
    }
}

Then $test will either be an instance of Icon_Set or some custom error object if it was configured improperly.

(OK, it's not a real factory method ... but similar enough ...)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment