Get Instance ID of an Object in PHP - php

I've learn a while ago on StackOverflow that we can get the "instance ID" of any resource, for instance:
var_dump(intval(curl_init())); // int(2)
var_dump(intval(finfo_open())); // int(3)
var_dump(intval(curl_init())); // int(4)
var_dump(intval(finfo_open())); // int(5)
var_dump(intval(curl_init())); // int(6)
I need something similar but applied to classes:
class foo {
public function __construct() {
ob_start();
var_dump($this); // object(foo)#INSTANCE_ID (0) { }
echo preg_replace('~.+#(\d+).+~s', '$1', ob_get_clean());
}
}
$foo = new foo(); // 1
$foo2 = new foo(); // 2
The above works but I was hoping for a faster solution or, at least, one that didn't involve output buffers. Please note that this won't necessarily be used within the constructor or even inside the class itself!
spl_object_hash() is not what I'm looking for because the two objects produce identical hashes
The question previously contained an incorrect example of spl_object_hash output; ensuring that both objects exist at the same time produces hashes which are subtly different:
var_dump(spl_object_hash($foo)); // 0000000079e5f3b60000000042b31773
var_dump(spl_object_hash($foo2)); // 0000000079e5f3b50000000042b31773
Casting to int like resources doesn't seem to work for objects:
Notice: Object of class foo could not be converted to int.
Is there a quick way to grab the same output without using object properties?
Besides var_dump(), I've discovered by trial and error that debug_zval_dump() also outputs the object instance, unfortunately it also needs output buffering since it doesn't return the result.

spl_object_hash() could help you out here. It
returns a unique identifier for the object
which is always the same for a given instance.
EDIT after OP comment:
You could implement such a behavior using a static class property, e.g:
class MyClass
{
private static $_initialized = false;
public function __construct()
{
if (!self::$_initialized) {
self::$_initialized = true;
// your run-only-once code
}
}
}
But actually this has nothing to with your original question.

Well, yes, with an extension.
Note that the handles used for objects that were, in the meantime, destroyed, can be reused.
Build with phpize && ./configure && make && make install
testext.h
#ifndef PHP_EXTTEST_H
# define PHP_EXTTEST_H
# ifdef HAVE_CONFIG_H
# include<config.h>
# endif
# include <php.h>
extern zend_module_entry testext_module_entry;
#define phpext_testext_ptr &testext_module_entry
#endif
testext.c
#include "testext.h"
PHP_FUNCTION(get_object_id)
{
zval *obj;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o", &obj)
== FAILURE) {
return;
}
RETURN_LONG(Z_OBJ_HANDLE_P(obj));
}
static zend_function_entry ext_functions[] = {
PHP_FE(get_object_id, NULL)
{NULL, NULL, NULL, 0, 0}
};
zend_module_entry testext_module_entry = {
STANDARD_MODULE_HEADER,
"testext",
ext_functions, /* Functions */
NULL, /* MINIT */
NULL, /* MSHUTDOWN */
NULL, /* RINIT */
NULL, /* RSHUTDOWN */
NULL, /* MINFO */
NO_VERSION_YET,
STANDARD_MODULE_PROPERTIES
};
ZEND_GET_MODULE(testext)
config.m4
PHP_ARG_ENABLE(testext,
[Whether to enable the "testext" extension],
[ enable-testext Enable "testext" extension support])
if test $PHP_EXTTEST != "no"; then
PHP_SUBST(EXTTEST_SHARED_LIBADD)
PHP_NEW_EXTENSION(testext, testext.c, $ext_shared)
fi
Test script
<?php
$a = new stdclass();
$b = new stdclass();
var_dump(get_object_id($a));
var_dump(get_object_id($b));
Output
int(1)
int(2)

Alix, your solution in the question was exactly what I needed, but actually breaks when there's an object in an object, returns the last # in the var_dump. I fixed this, made the regex faster, and put it in a nice little function.
/**
* Get global object ID
* From: http://stackoverflow.com/questions/2872366/get-instance-id-of-an-object-in-php
* By: Alix Axel, non-greedy fix by Nate Ferrero
*/
function get_object_id(&$obj) {
if(!is_object($obj))
return false;
ob_start();
var_dump($obj);// object(foo)#INSTANCE_ID (0) { }
preg_match('~^.+?#(\d+)~s', ob_get_clean(), $oid);
return $oid[1];
}

Have a look at spl_object_hash(). Usage example:
$id = spl_object_hash($object);
Note that you'll need PHP 5 >= 5.2.0 for that to work.

As long as you implement the base class all the classes you're going to need this from, you can do something like this:
class MyBase
{
protected static $instances = 0;
private $_instanceId = null;
public function getInstanceId()
{
return $this->_instanceId;
}
public function __construct()
{
$this->_instanceId = ++self::$instances;
}
}
class MyTest extends MyBase
{
public function Foo()
{
/* do something really nifty */
}
}
$a = new MyBase();
$b = new MyBase();
$c = new MyTest();
$d = new MyTest();
printf("%d (should be 1) \n", $a->getInstanceId());
printf("%d (should be 2) \n", $b->getInstanceId());
printf("%d (should be 3) \n", $c->getInstanceId());
printf("%d (should be 4) \n", $d->getInstanceId());
The output would be:
1 (should be 1)
2 (should be 2)
3 (should be 3)
4 (should be 4)

As of PHP 7.2 you can use spl_object_id
$id = spl_object_id($object);
$storage[$id] = $object;

What you're trying to do, is actually Aspect-Oriented Programming (AOP).
There are at least a couple of frameworks available for AOP in PHP at this point:
seasar (formerly PHPaspect) is a larger framework integrating with Eclipse - the screenshot shows you a little code snippet that answers your question, weaving some code around a particular new statement throughout a project.
php-aop is a lightweight framework for AOP.
typo3 has an AOP framework built in.
This may be overkill for your needs, but you may find that exploring the kind of thinking behind ideas like these will lead you down the rabbit hole, and teach you new ways to think about software development in general - AOP is a powerful concept, allowing you to program in terms of strategies and concerns, or "aspects".
Languages like PHP were designed to solve programming tasks - the concept of AOP was designed to solve a programmers' task. When normally you would need to think about how to ensure that a particular concern gets fulfilled every time in your code base, you can think of this as simply an "aspect" of how you're programming, implement it in those terms directly, and count on your concerns to be implemented every time.
It requires less discipline, and you can focus on solving the practical programming tasks rather than trying to architect your way through high-level structural code requirements.

I don't have the PECL runkit enabled to test this, but this may allow you to remove the constructor code from the class definition after the first time that an instance of the class has been created.
Whether you can remove the constructor from within the constructor would be an interesting experiment.

This is a bit late to the party but I didn't see this answer and just recently implemented something similar for a debugging class ( to handle circular references). As you guys may or may not know the normal printing functions such as var_export, have limited or no circular reference support.
As noted the spl_object_hash is unique per instance, the problem I had with it is that it is ugly. Not really suited to printing for my debugger as it's something like this 000000006ac56bae0000000044fda36f which can be hard to compare to say this 000000006ac56bae0000000044fda35f. So like the OP stated what I wanted was just a number of the instance ( I only really needed this on a per class basis ).
Therefor the simple solution for me was to do the following.
$class = get_class( $input );
$hash = spl_object_hash( $input );
if( !isset( $objInstances[ $class ] )){
$objInstances[ $class ] = array();
}
$output = 'object(%s) #%s (%s){%s}'; //class, instance, prop_count, props
if( false === ( $index = array_search($hash, $objInstances[ $class ] ) ) ){
$index = count($objInstances[ $class ]); //set init index for instance
$objInstances[ $class ][] = $hash;
// .... debugging code
$output = 'debugging result.', //sprintf
}else{
$output = sprintf( $output, $class, $index, 0, '#_CIRCULAR_REFRENCE_#');
}
Obviously the debugging code is way more complex, but the essential thing here is that by tracking the class and spl hash in $objInstances I can easily assign my own instance numbers outside of the class. This means I don't need some ugly hack ( that affects the class's code ) to get a reference number. Also, I don't need to display the "ugly" spl hash. Anyway my full code for this outputs something like this.
$obj = new TestObj();
$obj1 = new TestObj();
$obj->setProProp($obj1);
$obj1->setProProp($obj); //create a circular reference
object(TestObj) #0 (7){
["SOME_CONST":const] => string(10) 'some_const',
["SOMEOTHER_CONST":const] => string(16) 'some_other_const',
["SOME_STATIC":public static] => string(6) 'static',
["_PRO_STATIC":protected static] => string(10) 'pro_static',
["someProp":public] => string(8) 'someProp',
["_pro_prop":protected] => object(TestObj) #1 (7){
["SOME_CONST":const] => string(10) 'some_const',
["SOMEOTHER_CONST":const] => string(16) 'some_other_const',
["SOME_STATIC":public static] => string(6) 'static',
["_PRO_STATIC":protected static] => string(10) 'pro_static',
["someProp":public] => string(8) 'someProp',
["_pro_prop":protected] => object(TestObj) #0 (0){#_CIRCULAR_REFRENCE_#},
["_proProp":protected] => string(7) 'proProp'
},
["_proProp":protected] => string(7) 'proProp'
}
As you can see it's very easy to see where object(TestObj) #0 (0){#_CIRCULAR_REFRENCE_#} came from now. I wanted to keep this debugging code as close to the native var_dump
which outputs this.
object(TestObj)#7 (3) {
["someProp"]=> string(8) "someProp"
["_pro_prop":protected]=> object(TestObj)#10 (3) {
["someProp"]=> string(8) "someProp"
["_pro_prop":protected]=> *RECURSION*
["_proProp":protected]=> string(7) "proProp"
}
["_proProp":protected]=> string(7) "proProp"
}
The difference here is I needed the return as a string, not output to the browser. I also wanted to be able to show class constants, static properties, and private properties ( with flags to change what the debugger outputs, and the depth limit). And, I wanted a bit more information as to what the circular reference was instead of just *RECURSION* which doesn't tell me anything.
Hope it helps someone in the future.
Here is the full code for my Debug class, you can find this used about line #300
https://github.com/ArtisticPhoenix/Evo/blob/master/Evo/Debug.php

If you don't want to use output buffering... perhaps use var_export instead of var_dump?

Related

Query returns content from previous functions

I have a Chat.php file containing everything query-related. Then there is a Core.php containing connection to database and basic functions used in Chat.php like "query" and "rows" which processes the "query" into array.
In Chat.php there are two functions, the second one printing content of the first when using print_r($this->rows());. checkForLastMessage() is supposed to check one table to see if there are new messages to be pulled from another table with function getNewMessages().
This is how it looks like:
Core.php
<?php
class Core
{
...
public function query($sql)
{
$this->result = $this->db->query($sql);
}
public function rows()
{
for($x = 1; $x <= $this->db->affected_rows; $x++)
{
$this->rows[] = $this->result->fetch_assoc();
}
return $this->rows;
}
}
Chat.php
<?php
class Chat extends Core
{
public function checkForLatestMessage($chatid, $iam)
{
$userinchat='for'.$iam;
$this->query("SELECT anonchat.$userinchat FROM anonchat WHERE anonchat.chatid=$chatid");
$printarray = Array();
$printaray = '';
foreach( $this->rows() as $id )
{
$printarray[] = $id[$userinchat];
}
if($printarray[0] != '')
{
$this->getNewMessages($chatid, $printarray[0]);
}
}
public function getNewMessages($chatid, $requiredMessages)
{
$this->query("SELECT anonmessage.content, anonmessage.timeposted FROM anonmessage WHERE anonmessage.messageid IN ($requiredMessages) ORDER BY anonmessage.timeposted ASC");
print_r($this->rows());
}
The last print_r contains elements from the previous function. I don't know why that is.
Edit. This is the output:
Array ( [0] => Array ( [for1] => 2,4,6 ) [1] => Array ( [content] =>
Message 2 [timeposted] => 2017-08-04 16:12:34 ) [2] => Array (
[content] => Message 4 [timeposted] => 2017-08-04 16:12:48 ) [3] =>
Array ( [content] => Message 6 [timeposted] => 2017-08-04 16:13:03 ) )
Element [0] of array (the one with "for1") is remaining from previous function.
To answer your question, you're initializing the class property $this->rows in the first method and then the 2nd method is appending to it. You need to reset $this->rows before adding to it.
public function getNewMessages($chatid, $requiredMessages)
{
$this->rows = null;
$this->query("SELECT anonmessage...");
print_r($this->rows());
}
Or better yet, reset the variable in the query() method. That way you don't have to do it each time.
Please don't take offense when I say that this is a bad design.
You're trying to write your own DAL (Data Abstraction Layer). This is a very complicated task and there are already lots of implementations out there. The Core class is going to become massive, complicated, and unwieldy when you try to adapt it to a dozen other classes.
PHP only supports a single inheritance so right off the bat you shot your self in the foot because any class that needs DB interactions will have to extend Core and you won't be able to extend anything else.
Consider keeping things simple for now and let each method handle their own queries and DB interactions. Focus on major concepts like DRY, encapsulation and keeping your classes focused on their responsibility.
Ex. checkForLatestMessage() what is this supposed to do? It sounds like should check for messages and then return a boolean (true|false) but instead it is calling getNewMessages() which outputs some data.
I don't know enough about your application to really suggest something useful but this feels a bit better than the path you're heading down. Notice we're not inheriting from Core so you're free to inherit from something else. The methods are concise and do what they say. You'd probably also save a few lines of code this way and it's easier to read.
<?php
class Chat
{
public function hasNewMessages($chatid, $iam)
{
// Query using PDO properly
return (bool)$hasNewMessages;
}
public function getNewMessages($chatid, $requiredMessages)
{
// Query using PDO properly
// An array of data or objects
return $messages;
}
}
/******** Client Code ***********/
$chat = new Chat();
if ($chat->hasNewMessages()) {
foreach ($chat->getNewMessages($id, $required) as $message) {
// $message
}
}
Just some of my thoughts... good luck.

Recall chained methods on PHP

I call an object that returns an array given certain chained methods:
Songs::duration('>', 2)->artist('Unknown')->genre('Metal')->stars(5)->getAllAsArray();
The problem lies that every time I want to get this array, for example, in another script, I have to chain everything again. Now imagine that in over 10 scripts.
Is there a way to recall the chained methods for later use?
Since you can't cache the result, you could cache the structure of the call chain in an array.
$chain = [
'duration' => ['>', 2],
'artist' => 'Unknown',
'genre' => 'Metal',
'stars' => 5,
'getAllAsArray' => null
];
You could use that with a function that emulates the chained call using the cached array:
function callChain($object, $chain) {
foreach ($chain as $method => $params) {
$params = is_array($params) ? $params : (array) $params;
$object = call_user_func_array([$object, $method], $params);
}
return $object;
}
$result = callChain('Songs', $chain);
If you can not cache your results as suggested, as I commented, here are a couple ideas. If your application allows for mixing of functions (as in you are permitted by standards of your company's development rules) and classes, you can use a function wrapper:
// The function can be as complex as you want
// You can make '>', 2 args too if they are going to be different all the time
function getArtists($array)
{
return \Songs::duration('>', 2)->artist($array[0])->genre($array[1])->stars($array[2])->getAllAsArray();
}
print_r(getArtists(array('Unkown','Metal',5)));
If you are only allowed to use classes and __callStatic() is not forbidden in your development and is also available in the version of PHP you are using, you might try that:
// If you have access to the Songs class
public __callStatic($name,$args=false)
{
// This should explode your method name
// so you have two important elements of your chain
// Unknown_Metal() should produce "Unknown" and "Metal" as key 0 and 1
$settings = explode("_",$name);
// Args should be in an array, so if you have 1 value, should be in key 0
$stars = (isset($args[0]))? $args[0] : 5;
// return the contents
return self::duration('>', 2)->artist($settings[0])->genre($settings[1])->stars($stars)->getAllAsArray();
}
This should return the same as your chain:
print_r(\Songs::Unknown_Metal(5));
It should be noted that overloading is hard to follow because there is no concrete method called Unknown_Metal so it's harder to debug. Also note I have not tested this particular set-up out locally, but I have notated what should happen where.
If those are not allowed, I would then make a method to shorten that chain:
public function getArtists($array)
{
// Note, '>', 2 can be args too, I just didn't add them
return self::duration('>', 2)->artist($array[0])->genre($array[1])->stars($array[2])->getAllAsArray();
}
print_r(\Songs::getArtists(array('Unkown','Metal',5)));
I wrote a lib doing exactly what you're looking for, implementing the principle suggested by Don't Panic in a high quality way: https://packagist.org/packages/jclaveau/php-deferred-callchain
In your case you would code
$search = DeferredCallChain::new_(Songs::class) // or shorter: later(Songs::class)
->duration('>',2) // static syntax "::" cannot handle chaining sadly
->artist('Unknown')
->genre('Metal')
->stars(5)
->getAllAsArray();
print_r( $search($myFirstDBSongs) );
print_r( $search($mySecondDBSongs) );
Hoping it will match your needs!

PHP: Sending a list of options as an argument (alternative to named parameters/ argument bag)

I wish to give a list of options as an argument to a function.
The Ideal Scenario: Named Parameters
If PHP has named parameters it would be done like so:
function setOptions($title, $url, $public = true, $placeholder = "type here...") {
...
}
setOptions($title = "Hello World", $url = "example.com", $placeholder = "hi");
Unfortunately PHP does not have named parameters (please tell me if PHP7 is planned to have some as a comment).
The solution everyone else is using: Associative Array
Most PHP scripts I have seen use an alternative array approach like so:
function setOptions($options) {
...
}
setOptions(array(
'title' => "Hello World",
'url' => "example.com",
'placeholder' => "hi"
));
Drawbacks of Associative Array Approach
Although this works fine, there are the following drawbacks:
The user does not benefit from autocompletion (taking a long time to write)
The user can easily makes mistakes in spellings
The don't know what options is available, so may frequently revert back to documentation
Is there a better way?
Is there a better way that can address these issues (either in current PHP or PHP7 or maybe even hacklang(?)).
In Hack, you can use Shapes. Shapes define a structure for associative arrays so that things can be autocompleted (depending on IDE support) and spelling mistakes are picked up by the type checker.
For instance, your example could be reworked like:
function setOptions(shape(
'title' => string,
'url' => string,
'public' => ?bool,
'placeholder' => ?string,
) $options) {
$title = $options['title'];
$url = $options['url'];
$public = Shapes::idx($options, 'public', true);
$placeholder = Shapes::idx($options, 'placeholder', 'type here...');
...
}
setOptions(shape(
'title' => 'Hello World',
'url' => 'example.com',
'placeholder' => 'hi',
));
This marks title and url to both be required options and public and placeholder are optional (all nullable types in shapes are considered to be optional). Shapes::idx is then used to get the value provided, or the default value (the third argument) if a value was not passed in.
Solution: Using fluent setters
A potential solution I have found to this problem is to use classes and fluent setters like so:
class PostOptions {
protected
$title,
$url,
$public = TRUE,
$placeholder = "type here..."; //Default Values can be set here
static function getInstance(): PostOptions {
return new self();
}
public function setTitle($title) {
$this->title = $title;
return $this;
}
public function setUrl($url) {
$this->url = $url;
return $this;
}
public function setPublic($public) {
$this->public = $public;
return $this;
}
public function setPlaceholder($placeholder) {
$this->placeholder = $placeholder;
return $this;
}
}
You can then send the options like so:
function setOptions(PostOptions $postOptions) {
//...
}
setOptions(
PostOptions::getInstance()
->setTitle("Hello World")
->setUrl("example.com")
->setPlaceholder("hi")
);
Doing it quickly! (This looks long)
Although this may look long, it can actually be implemented VERY quickly using IDE tools.
e.g. In InteliJ or PHPStorm, just type ALT+INS > Select setters > Select the fields you want to set and check the checkbox for fluent setters > click OK
Why Fluent Setters? Why Not just make all the fields public?
Using public fields is a LOT slower. This is because fluent setters can make use of chained methods, whilst the public fields way must be written like this:
$options = new PostOptions();
$options->title = "hello";
$options->placeholder = "...";
$options->url "..."
setOptions($options);
Which is a lot more typing compared to the proposed solution
Why is this better?
It's faster in IDE's when using autocomplete than the array approach
Unlikely to make mistakes in spellings (thanks to autocomplete)
Easy to see what options is available (again thanks to autocomplete)
Can give individual documentation for individual fields using PHPDoc
Can use nested options more easily e.g. If you had a list of options, and that option also had more list of options
Other OOP advantages e.g. Inheritance & Abstract Classes
How much faster is this approach?
I implemented a quick class for Wordpress labels array in: https://codex.wordpress.org/Function_Reference/register_post_type
I found that setting a property for each value (with the documentation next to you on a 2nd monitor) that the fluent setters approach is approximately 25% faster than the array approach thanks to autocomplete! However, if the documentation was not next to you, I expect this approach will far exceed 25%, as discovery of options is much quicker!
Alternative approaches are welcome
Declaration from array
This is how I normally declare my class structure. The only drawback is that it takes a while longer to write, but it allows optional parameters, defaults values, etc.
public static $defaults = array(
'user_id' => null,
'username' => null,
'avatar' => null,
'email' => null,
'description' => null,
);
public function __construct(array $args = array()) {
$this->dbc = Database::connection();
$defaults = self::$defaults;
$args = array_merge($defaults, $args);
//Assign the object properites
$this->user_id = (is_numeric($args['user_id'])) ? $args['user_id'] : null;
$this->username = $args['username'];
$this->avatar = AVATAR_DIR . $args['avatar'];
$this->email = $args['email'];
$this->description = $args['description'];
}
This way, you can declare an object like $x = new User(), and it will work perfectly fine. Let's say you've only selected a few columns from your SQL statement. You can make the keys in the public static $defaults into the same name as the columns you've selected, that way to instantiate your object, you can easily do:
$row = mysqli_fetch_array($result, MYSQLI_ASSOC);
$object = new User($row);
The array_merge takes care of having any extraneous keys that you don't need in the argument they provided. If you need to change options, you can declare them the same way for __construct() with a default array and array_merge to catch arguments and mimic named parameters and defaults values (like in Python)
With Syntactic: https://github.com/topclaudy/php-syntactic
you can just do:
function foo($a = 1, $b = 2, $c = 3, $d = 4){
return $a * $b * $c * $d;
}
And call it with the arguments you want:
//Call with argument b only
echo s('foo')->in('b', 5)->out(); //Outputs 60
//Call with argument a and argument at index/position 1 (b),
echo s('foo')->in('a', 7)->in(1, 5)->out(); //Outputs 420
//Call with argument c only through dynamic method
echo s('foo')->c(9)->out(); //Outputs 72
If U have that much parameters I'd think about creating an object that you'll pass to class instead of n parameters and every parameter is one field there. In constructor you put required parameters and this is then clean solution.

PHP - best way to initialize an object with a large number of parameters and default values

I'm designing a class that defines a highly complex object with a ton (50+) of mostly optional parameters, many of which would have defaults (eg: $type = 'foo'; $width = '300'; $interactive = false;). I'm trying to determine the best way to set up the constructor and instance/class variables in order to be able to:
make it easy to use the class
make it easy to auto-document the class (ie: using phpDocumentor)
code this elegantly
In light of the above, I don't want to be passing the constructor a ton of arguments. I will be passing it a single hash which contains the initialization values, eg: $foo = new Foo(array('type'=>'bar', 'width'=>300, 'interactive'=>false));
In terms of coding the class, I still feel like I would rather have...
class Foo {
private $_type = 'default_type';
private $_width = 100;
private $_interactive = true;
...
}
...because I believe this would facilitate documentation generation (you get the list of the class' properties, which lets the API user know what 'options' they have to work with), and it "feels" like the right way to do it.
But then you run into the problem of mapping the incoming parameters in the constructor to the class variables, and without exploiting the symbol table, you get into a "brute force" approach which to me defeats the purpose (though I'm open to other opinions). E.g.:
function __construct($args){
if(isset($args['type'])) $_type = $args['type']; // yuck!
}
I've considered creating a single class variable that is itself an associative array. Initializing this would be really easy then, e.g.:
private $_instance_params = array(
'type' => 'default_type',
'width' => 100,
'interactive' => true
);
function __construct($args){
foreach($args as $key=>$value){
$_instance_params[$key] = $value;
}
}
But this seems like I'm not taking advantage of native features like private class variables, and it feels like documentation generation will not work with this approach.
Thanks for reading this far; I'm probably asking a lot here, but I'm new to PHP and am really just looking for the idiomatic / elegant way of doing this. What are your best practices?
Addendum (details about this particular Class)
It's quite likely that this class is trying to do too much, but it is a port of an old Perl library for creating and processing forms. There's probably a way of dividing the configuration options to take advantage of inheritance and polymorphism, but it may actually be counter-productive.
By request, here is a partial listing of some of the parameters (Perl code). You should see that these don't map very well to sub-classes.
The class certainly has getters and setters for many of these properties so the user can over-ride them; the objective of this post (and something the original code does nicely) is to provide a compact way of instantiating these Form objects with the required parameters already set. It actually makes for very readable code.
# Form Behaviour Parameters
# --------------------------
$self->{id}; # the id and the name of the <form> tag
$self->{name} = "webform"; # legacy - replaced by {id}
$self->{user_id} = $global->{user_id}; # used to make sure that all links have the user id encoded in them. Usually this gets returned as the {'i'} user input parameter
$self->{no_form}; # if set, the <form> tag will be omitted
$self->{readonly}; # if set, the entire form will be read-only
$self->{autosave} = ''; # when set to true, un-focusing a field causes the field data to be saved immediately
$self->{scrubbed}; # if set to "true" or non-null, places a "changed" radio button on far right of row-per-record forms that indicates that a record has been edited. Used to allow users to edit multiple records at the same time and save the results all at once. Very cool.
$self->{add_rowid}; # if set, each row in a form will have a hidden "rowid" input field with the row_id of that record (used primarily for scrubbable records). If the 'scrubbed' parameter is set, this parameter is also automatically set. Note that for this to work, the SELECT statement must pull out a unique row id.
$self->{row_id_prefix} = "row_"; # each row gets a unique id of the form id="row_##" where ## corresponds to the record's rowid. In the case of multiple forms, if we need to identify a specific row, we can change the "row_" prefix to something unique. By default it's "row_"
$self->{validate_form}; # parses user_input and validates required fields and the like on a form
$self->{target}; # adds a target window to the form tag if specified
$self->{focus_on_field}; # if supplied, this will add a <script> tag at the end of the form that will set the focus on the named field once the form loads.
$self->{on_submit}; # adds the onSubmit event handler to the form tag if supplied
$self->{ctrl_s_button_name}; # if supplied with the name of the savebutton, this will add an onKeypress handler to process CTRL-S as a way of saving the form
# Form Paging Parameters
# ----------------------
$self->{max_rows_per_page}; # when displaying a complete form using printForm() method, determines the number of rows shown on screen at a time. If this is blank or undef, then all rows in the query are shown and no header/footer is produced.
$self->{max_pages_in_nav} = 7; # when displaying the navbar above and below list forms, determines how many page links are shown. Should be an odd number
$self->{current_offset}; # the current page that we're displaying
$self->{total_records}; # the number of records returned by the query
$self->{hide_max_rows_selector} = ""; # hide the <select> tag allowing users to choose the max_rows_per_page
$self->{force_selected_row} = ""; # if this is set, calls to showPage() will also clear the rowid hidden field on the form, forcing the first record to be displayed if none were selected
$self->{paging_style} = "normal"; # Options: "compact"
We can, of course, allow ourselves to be drawn into a more lengthy debate around programming style. But I'm hoping to avoid it, for the sanity of all involved! Here (Perl code, again) is an example of instantiating this object with a pretty hefty set of parameters.
my $form = new Valz::Webform (
id => "dbForm",
form_name => "user_mailbox_recip_list_students",
user_input => \%params,
user_id => $params{i},
no_form => "no_form",
selectable => "checkbox",
selectable_row_prefix => "student",
selected_row => join (",", getRecipientIDsByType('student')),
this_page => $params{c},
paging_style => "compact",
hide_max_rows_selector => 'true',
max_pages_in_nav => 5
);
I can think of two ways of doing that. If you want to keep your instance variables you can just iterate through the array passed to the constructor and set the instance variable dynamically:
<?php
class Foo {
private $_type = 'default_type';
private $_width = 100;
private $_interactive = true;
function __construct($args){
foreach($args as $key => $val) {
$name = '_' . $key;
if(isset($this->{$name})) {
$this->{$name} = $val;
}
}
}
}
?>
When using the array approach you don't really have to abandon documentation. Just use the #property annotations in the class body:
<?php
/**
* #property string $type
* #property integer $width
* #property boolean $interactive
*/
class Foo {
private $_instance_params = array(
'type' => 'default_type',
'width' => 100,
'interactive' => true
);
function __construct($args){
$this->_instance_params = array_merge_recursive($this->_instance_params, $args);
}
public function __get($name)
{
return $this->_instance_params[$name];
}
public function __set($name, $value)
{
$this->_instance_params[$name] = $value;
}
}
?>
That said, a class with 50 member variables is either only used for configuration (which can be split up) or it is just doing too much and you might want to think about refactoring it.
Another approach is to instantiate the class with a FooOptions object, acting solely as an options container:
<?php
class Foo
{
/*
* #var FooOptions
*/
private $_options;
public function __construct(FooOptions $options)
{
$this->_options = $options;
}
}
class FooOptions
{
private $_type = 'default_type';
private $_width = 100;
private $_interactive = true;
public function setType($type);
public function getType();
public function setWidth($width);
public function getWidth();
// ...
}
Your options are well documented and you have an easy way to set/retrieve them. This even facilitates your testing, as you can create and set different options objects.
I don't remember the exact name of this pattern, but I think it's Builder or Option pattern.
Just to follow up with how I implemented this, based on one of Daff's solutions:
function __construct($args = array()){
// build all args into their corresponding class properties
foreach($args as $key => $val) {
// only accept keys that have explicitly been defined as class member variables
if(property_exists($this, $key)) {
$this->{$key} = $val;
}
}
}
Improvement suggestions welcomed!
You also could make a parent class.
In that class you only define the variables.
protected function _SetVarName( $arg ){
$this->varName=$arg;
}
Then extend that class into a new file and in that file you create all your processes.
So you get
classname.vars.php
classname.php
classname extends classnameVars {
}
Because most will be on default you only have to Set/Reset the ones you need.
$cn=new classname();
$cn->setVar($arg);
//do your functions..
I use this on a few of my classes. Makes it easy to copy and paste for rapid development.
private $CCNumber, $ExpMonth, $ExpYear, $CV3, $CardType;
function __construct($CCNumber, $ExpMonth, $ExpYear, $CV3, $CardType){
$varsValues = array($CCNumber, $ExpMonth, $ExpYear, $CV3, $CardType);
$varNames = array('CCNumber', 'ExpMonth', 'ExpYear', 'CV3', 'CardType');
$varCombined = array_combine($varNames, $varsValues);
foreach ($varCombined as $varName => $varValue) {$this->$varName = $varValue;}
}
Steps to use:
Paste in and get the list of variables from your current __construct function, removing any optional parameter values
If you haven't already, paste that in to declare your variables for your class, using the scope of your choosing
Paste that same line into the $varValues and $varNames lines.
Do a text replace on ", $" for "', '". That'll get all but the first and last that you'll have to manually change
Enjoy!
Just a little improvement on Daff's first solution to support object properties that may have a null default value and would return FALSE to the isset() condition:
<?php
class Foo {
private $_type = 'default_type';
private $_width = 100;
private $_interactive = true;
private $_nullable_par = null;
function __construct($args){
foreach($args as $key => $val) {
$name = '_' . $key;
if(property_exists(get_called_class(),$name))
$this->{$name} = $val;
}
}
}
}
?>

PHP architecture, and pass-by-reference vs pass-by-value

Seeking suggestions from PHP architects!
I'm not terribly familiar with PHP but have taken over maintenance of a large analytics package written in the language. The architecture is designed to read reported data into large key/value arrays, which are passed through various parsing modules to extract those report parameters known to each of those modules. Known parameters are removed from the master array, and any leftovers which were not recognized by any of the modules, are dumped into a kind of catch-all report showing the "unknown" data points.
There are a few different methods being used to call these parser modules, and I would like to know which if any are considered to be "proper" PHP structure. Some are using pass-by-reference, others pass-by-value, some are functions, some are objects. All of them modify the input parameter in some way.
A super-simplified example follows:
#!/usr/bin/php
<?php
$values = Array("a"=>1, "b"=>2, "c"=>3, "d"=>4 );
class ParserA {
private $a = null;
public function __construct(&$myvalues) {
$this->a = $myvalues["a"];
unset($myvalues["a"]);
}
public function toString() { return $this->a; }
}
// pass-by-value
function parse_b($myvalues) {
$b = $myvalues["b"];
unset($myvalues["b"]);
return Array($b, $myvalues);
}
// pass-by-reference
function parse_c(&$myvalues) {
echo "c=".$myvalues["c"]."\n";
unset($myvalues["c"]);
}
// Show beginning state
print_r($values);
// will echo "1" and remove "a" from $values
$a = new ParserA($values);
echo "a=".$a->toString()."\n";
print_r($values);
// w ill echo "2" and remove "b" from $values
list($b, $values) = parse_b($values);
echo "b=".$b."\n";
print_r($values);
// will echo "3" and remove "c" from $values
parse_c($values);
print_r($values);
?>
The output will be:
Array
(
[a] => 1
[b] => 2
[c] => 3
[d] => 4
)
a=1
Array
(
[b] => 2
[c] => 3
[d] => 4
)
b=2
Array
(
[c] => 3
[d] => 4
)
c=3
Array
(
[d] => 4
)
I'm really uncomfortable having so many different call methods in use, some of which have hidden effects on the call function parameters using "&pointer"-style functions, some requiring the main body to write their output, and some writing their output independently.
I would prefer to choose a single methodology and stick with it. In order to do so, I would also like to know which is most efficient; my reading of the PHP documentation indicates that since it uses copy-on-write, there shouldn't be much performance difference between using pointers to vs passing the object directly and re-reading a return value. I would also prefer to use the object-oriented structure, but am uncomfortable with the hidden changes being made to the input parameter on the constructor.
Of the three calling methods, ParserA(), parse_b(), and parse_c(), which if any is the most appropriate style?
I'm not really an expert in PHP but from my experience passing by value is better. This way code won't have side effects and that mean it will be easier to understand and maintain and do all sorts of crazy things on it, like using it as callback for map function. So I'm all for parse_b way of doing things.
FYI: In PHP, objects are always passed by reference, no matter what. Also if you have an array with objects and scalar values in it, the scalar values are passed by value, but the objects by reference.
As a general rule in PHP, do not use references unless you really have to.
references in PHP are also not what most people expect them to be:
"References in PHP are a means to access the same variable content by different names. They are not like C pointers; instead, they are symbol table aliases.""
see also: php.net: What References Are
So in short:
The proper way of handling this PHP is using creating an object that passes the variables around by value or manipulating the array with array_map (array_map allows you to apply a callback function to the elements an array.)
I would vote against the methods proposed in general, but of them, I think parse_b has the best idea.
I think it would be better design to wrap the "data" array in a class that could let you "pop" a key out of it easily. So the parser ends up looking like:
class ParserA {
private $a = null;
public function __construct(My_Data_Class $data) {
$this->a = $data->popValue("a");
}
public function toString() { return $this->a; }
}
And a sample implementation
class My_Data_Class {
protected $_data;
public function __construct(array $data) {
$this->_data = $data;
}
public function popValue($key) {
if (isset($this->_data[$key])) {
$value = $this->_data[$key];
unset($this->_data[$key]);
return $value;
}
}
}

Categories