I've looked at similar questions like this, and none of the solutions offered within them seems to answer my question.
This is the code I have so far (just learning OOP):
<?php
class page {
var $ot;
function begin(){
$this->ot = '';
}
function finish(){
echo $this->ot;
}
class forms extends page {
function __construct($form_action, $form_type){
$this->ot .= '<form action=' . $form_action . ' method=' . $form_type . ' />';
}
function create_input($type, $name){
$this->ot .= '<input type="' . $type . '" name="' . $name . '" /><br />';
}
function create_submit($value){
$this->ot .= '<input type="submit" value="' . $value . '" />';
}
function __destruct(){
$this->ot .= '</form>';
}
}
class labels extends page {
function create_label($label){
$this->ot .= '<label>' . $label . ' </label>';
}
}
$page = new page();
$page->begin();
$newform = new forms('/class_lib.php', 'GET');
$newlabels = new labels();
$newlabels->create_label('Username:');
$newform->create_input('text', 'username');
$newlabels->create_label('Password:');
$newform->create_input('password', 'password');
$page->finish();
?>
For some reason, this code does not output anything to the browser. However, if I change the child classes forms and labels to echo their output instead of storing it in the parent variable the code seems to spring to life and works as intended.
Excuse my ignorance as I am new to OOP.
Thanks!
$ot is an object-property. This means, that every object of the class page or any subclass has its own "version" of $ot. Now you instanciate some objects and set some values, but at the end, when you call $page->finish(); $page->ot is empty anyway.
In OOP you have the classes (types) and instances of the classes (objects). The $ot property is what you call an instance variable, it belongs to the instances (objects) you create and is not a property of the class itself.
By making forms a subclass of page, you get what you call a "is a" relationship between the classes. That means that forms will inherit the structure of the page class. Modifying the property of a subclass object will not affect the superclass objects or any other object in this case.
When you first create a page object, that object has a $ot property. When you create a object of type forms, that object has its own $ot property.
To understand the concepts of OOP I would recommend you to read some tutorials. You could start by reading the Class, Instance and Inheritance parts of this wikipedia article:
http://en.wikipedia.org/wiki/Object-oriented_programming
The objects are not really connected.
You create three new objects, but they never reference each other.
Try this
// Create a new form
$newform = new forms('/class_lib.php', 'GET');
$newform->create_input('text', 'username');
$newform->create_input('password', 'password');
// Output the form, it can use the finish() method because it extends page
$newform->finish();
This will work and output the <input> elements, but your label class isn't plugged in to the $newForm to do anything, its just created and is completely separate.
EDIT - bored this evening....
You will need PHP5 to run this, its not perfect, but its a good start! I have defined the following an interface called renderable and classes called element, input, label and form
// An interface describes the methods that a class must use
interface renderable
{
// Any classes that implement the renderabe interface must define a method called render()
function render();
}
// This abstract class can never be created, so you can never do new element(), it implements renderable
abstract class element implements renderable
{
// Set up some variables for all elemnts
var $attribs = array();
var $name = "";
var $type = "";
// The construct for a element needs a type and a name
function __construct($type, $name)
{
$this->name = $name;
$this->type = $type;
}
// Set an attribute for the element
function setAttribute($name, $value)
{
$this->attribs[$name] = $value;
}
// Get the name of this element
function getName()
{
return $this->name;
}
// The render function outputs an element
function render()
{
// Output the start of the element eg <input
echo "<" . $this->type . " ";
// each attribute eg class='blue'
foreach($this->attribs as $name => $value)
echo " " . $name . "='" . $value ."' ";
// end the element
echo " />";
echo "<br />";
}
}
// The input element extends element but is not abstract
class input extends element
{
// Nothing is overridden here from the parent class element
}
// The label element extends element but is not abstract
class label extends element
{
// Define a new var called label, this is special for the label element
var $label = "";
// Override the contruct for element to only accept a name, this
// is because the label element type will always be label
function __construct($name)
{
$this->name = $name;
$this->type = "label";
}
// Set the label var
function setLabel($label)
{
$this->label = $label;
}
// Override the render function, this means that label has its own render function
// and does not use the function from the abstract class element
function render()
{
echo "<" . $this->type . " ";
foreach($this->attribs as $name => $value)
echo " " . $name . "='" . $value ."' ";
echo " >";
// Here the special label content is displayed
echo $this->label;
echo "</label>";
}
}
// A form extends element
class form extends element
{
// A form has some new vars
var $elements = array();
var $labels = array();
var $action;
var $method;
// Override the contruct and use name, action and method
// There are default values for action and method so they are not required
function __construct($name, $action = "/", $method = "GET")
{
$this->name = $name;
$this->type = "form";
$this->action = $action;
$this->method = $method;
}
// Add a new element to the form along with its label
function appendElement($element, $label)
{
// Add these to an array inside this class
$this->elements[$element->getName()] = $element;
$this->labels[$label->getName()] = $label;
}
// Override the render function
function render()
{
// Output the form's start along with the method and action
echo '<' . $this->type. ' ' . 'action="' . $this->action . '" method="' . $this->method . '" />';
// Iterate over the array of elments and render each one
foreach($this->elements as $name => $ele)
{
// Render the label for the current element
$this->labels[$name]->render();
// Render the element
$ele->render();
}
// End the form
echo "</form>";
}
}
// Create form with name, action and method
$form = new form("login", "/login.php", "POST");
// Create input username
$ele = new input("input", "username");
// Set type
$ele->setAttribute("type", "text");
// Set a class
$ele->setAttribute("class", "blue");
// Create a label for the username long with its content
$label = new label("username");
$label->setLabel("Username: ");
// Add the username element and its label
$form->appendElement($ele, $label);
// Repeat for password
$ele = new input("input", "password");
$ele->setAttribute("type", "password");
$label = new label("password");
$label->setLabel("Password: ");
$form->appendElement($ele, $label);
// Render the form
$form->render();
Since you're learning OOP, it's time to learn about Abstract Classes (PHP Manual)!
An abstract class is a sort of skeleton class that defines a series of generic functions. An abstract class can never be instantiated (i.e., you cannot call new AbstractClass), but can be extended by other classes. This allows us to define something generic and repeatable, say, and HTML Element, and then extend that to specific HTML elements as time goes on. Here is a sample implementation of that concept.
WARNING: I am not saying that this implementation is a great idea; learning purposes only!
First, some abstract classes to define how this stuff ought to work.
abstract class HTMLWriter
{
protected $html = '';
protected $tagName = null;
protected $selfClosing = false;
protected $elements = array();
protected $attributes = array();
protected $closed = false;
abstract public function __construct();
public function addElement(HTMLWriter $element)
{
if ($this->closed || $this->selfClosing) {
return;
}
$element->close(); // automatic!
$this->elements[] = $element->write();
}
public function addElements() {
foreach (func_get_args() as $arg) {
if ($arg instanceof HTMLWriter) {
$this->addElement($arg);
}
}
}
public function addAttribute($name, $value)
{
return $this->attributes[$name] = $value;
}
public function write()
{
if (!$this->closed) {
$this->close();
}
return $this->html;
}
public function close()
{
$this->closed = true;
$this->html = '<' . $this->tagName;
foreach ($this->attributes AS $attr => $val) {
$this->html .= ' ' . $attr . '="' . $val . '"';
}
if ($this->selfClosing) {
$this->html .= '/>';
return;
}
$this->html .= '>';
foreach($this->elements as $elem) {
$this->html .= $elem;
}
$this->html .= '</' . $this->tagName . '>';
}
}
abstract class HTMLWriterWithTextNodes extends HTMLWriter
{
//abstract public function __construct();
public function addText($text)
{
$this->elements[] = htmlentities($text);
}
public function addTextRaw($text)
{
$this->elements[] = $text;
}
}
And then the concrete implementations of those classes:
note: a concrete class is any non-abstract class, although this term loses its meaning when applied to classes that are not extensions of abstract classes.
class Form extends HTMLWriter
{
public function __construct($action, $method, $can_upload = false)
{
$this->tagName = 'form';
$this->addAttribute('action', $action);
$this->addAttribute('method', $method);
if ($can_upload) {
$this->addAttribte('enctype','multipart/form-data');
}
}
}
class Input extends HTMLWriter
{
public function __construct($type, $name, $id = null)
{
$this->tagName = 'input';
$this->selfClosing = true;
$this->addAttribute('type', $type);
$this->addAttribute('name', $name);
if (!is_null($id)) {
$this->addAttribute('id', $id);
}
}
// overrides
public function addElement()
{
return false;
}
}
class Label extends HTMLWriterWithTextNodes
{
public function __construct($labelText = null, $for = null)
{
$this->tagName = 'label';
if (!is_null($labelText)) {
$this->elements[] = $labelText;
}
if (!is_null($for)) {
$this->addAttribute('for', $for);
}
}
}
class GenericElement extends HTMLWriterWithTextNodes
{
public function __construct($tagName, $selfClosing = false)
{
if (empty($tagName)) {
$this->closed = true;
$this->html = '';
return;
}
$this->tagName = $tagName;
$this->selfClosing = (bool)$selfClosing;
}
}
Finally, let's instantiate and use our new classes
$form = new Form('/class_lib.php','get');
$username = new Input('text','username','username');
$password = new Input('password','password','password');
$submit = new Input('submit','login');
$submit->addAttribute('value','login');
$ulabel = new Label('Username: ', 'username');
$plabel = new Label('Password: ','password');
$br = new GenericElement('br',true);
$form->addElements(
$ulabel, $username, $br,
$plabel, $password, $br,
$submit
);
echo $form->write();
Output:
<form action="/class_lib.php" method="get"><label for="username">Username: </label><input type="text" name="username" id="username"/><br/><label for="password">Password: </label><input type="password" name="password" id="password"/><br/><input type="submit" name="login" value="login"/></form>
Hooray for abstract classes!
Related
Why I am able to use properties in constructor function without being defined in the class, Please read my code below.
<?php
class TV
{
public function __construct($m, $v)
{
$this->model = $m;
$this->volume = $v;
}
}
$tv_one = new TV("Samsung", 6);
echo $tv_one->model."<br><br>";
echo $tv_one->volume;
?>
Have a look at this code also. I am able to share private property outside the class. Just go throuh this code-
<?php
class TV
{
private $model = "Samsung";
private $volume = 2;
public function volumeUp()
{
return $this->volume++;
}
public function volumeDown()
{
return $this->volume--;
}
public function __construct($m, $v)
{
$this->model = $m;
$this->volume = $v;
}
}
class PlasmaTv extends TV
{
public $plasma = true;
public function hello()
{
return "I am new " . $this->model . " and my default volume is " . $this->volume . ".";
}
public function __construct($m, $v, $p)
{
$this->model = $m;
$this->volume = $v;
$this->plasma = $p;
}
}
$plasma = new PlasmaTv("Soni", 6, true);
echo $plasma->model."<br><br>";
echo $plasma->volume."<br><br>";
echo $plasma->plasma;
echo $plasma->hello();
?>
PHP doesn't require you to declare properties. Just assigning to a property will create it as a public property. So when the first constructor does:
$this->model = $m;
that creates a model property in the object.
In your PlasmaTv class, the model and volume properties are not the same as the ones in the parent class TV, because the properties in the parent are private and can't be accessed in the child. If you do:
$plasma->volumeUp();
echo $plasma->volume;
it will print 6, not 7, because volumeUp() incremented a different property than the public property in PlasmaTv.
If you want to share the properties between the child and parent, declare them protected. But then you won't be able to use $plasma->volume from outside the classes.
i'm writing a php class that is like an orm.
I have a method, that can be called statically or instanciated, and it must work in both cases.
Can you see what's wrong.
Basically is an object called Model.
When created it creates a table based on the inherited class.
For example:
Podcast extends Model ....
There are some functions like this that needs to be called statically and dynamically.
for example:
$podcastList = Podcast::findAll($db);
I get all podcasts objects from DB without need to have a podcast object instanciated.
But i can also do:
$podcast = new Podcast($db)
$podcastList = $podcast->findAll(); //no db here.... passed before
$db is a class i wrote to make operation on Database. IT simply does with OOP, what mysql_* do with functions. I'm not using PDO, i may use in future, but now i use mysql_* :P
that are the incriminated functions
public static function findAll($db=NULL, $self=NULL) {
if($self == NULL) {
$self = new static($db);
} else {
$self = $this;
}
$self->tableName = "";
$self->db = NULL;
$is_static = !(isset($this) && get_class($this) == __CLASS__);
if($is_static) {
//die(__CLASS__ . "::" . __FUNCTION__ . " CALLED STATICALLY");
if(!$self->db) {
die(__CLASS__ . "::" . __FUNCTION__ . " CALLED STATICALLY AND DB IS NULL");
//It stops here!
}
$self->tableName = $self->genTableName();
} else {
$self->db = $this->db;
$self->tableName = $this->tableName;
}
$query = "SELECT * FROM {$self->tableName}";
$r = $self->db->exec($query);
if(!$r) {
die(__CLASS__ . ":Error " . __FUNCTION__ . " record: " . $self->db->getError());
}
if($self->db->countRows($r) == 0) {
return NULL;
}
$objects = array();
while($row = $self->db->fetch($r, DBF::FETCH_ASSOC)) {
$objectClass = __CLASS__;
$object = new $objectClass($this->db);
//TODO Do it dinamically indipendently of column name
$f = get_class_vars($objectClass);
foreach ($f as $field => $value) {
$chuncks = explode("_", $field);
if($chuncks[0] == "f") {
$object->{$field} = $row[$chuncks[2]];
}
}
$objects[] = $object;
}
return $objects;
}
public function __call($name, $arguments) {
if ($name === 'findAll'){
return static::findAll($arguments, $this);
}
}
Both are part of a class.
Thank you for the help !
There's a lot wrong with this code. More important than your many logic mistakes (why are you setting $self = $this, then $self->db = NULL, then $self->db = $this->db?) is that you are misunderstanding what it means to be able to call static functions dynamically in PHP. The object $this simply doesn't exist in a static method. The call $podcast->findAll() looks non-static, but it's still static.
To do what you want to do, here are some options:
leave the function static and call findAll($this->db, $tablename) as needed
put the function into the db class and call it with parameter tablename
EDIT:
The second in my list is how I would do it. This is because you already have to have a db object in your original example, and there is nothing in particular that makes the function's purpose only suited to Podcast objects and not to, say, any other object representing database rows.
//calling examples:
$podcastlist = $db->findAll('Podcast');
$podcast = new Podcast($db);
$podcastlist = $podcast->findAll();
public class db {
....
function findAll($classname, $tablename=NULL) {
if(!isset($tablename)) {
//let's pretend you put default table names as class constants
$tablename = get_constant($classname.'::DEFAULT_TABLE');
}
$query = "SELECT * FROM {$tableName}";
$r = $this->exec($query);
if(!$r) {
throw new Exception("Error " . __FUNCTION__ . " record: " . $this->getError());
}
if($this->countRows($r) == 0) {
return NULL;
}
$objects = array();
while($row = $this->fetch($r, DBF::FETCH_ASSOC)) {
$object = new $classname($this);
//the following is an easier way to do your original foreach
foreach($row as $field=>$value) {
if(property_exists($classname, "f_".$field)) {
$object->{'f_'.$field} = $value;
}
}
$objects[] = $object;
}
//something you forgot:
return $objects;
}
}
public class Podcast extends Model {
....
public function findAll($tablename=NULL) {
return $this->db->findAll(class_name($this), $tablename);
}
}
I understand that one can use interfaces to mandate the definition of a function, but I cannot find something that enables one to mandate function calls, such that e.g. if I create a class being a member of another class (via extends, etc), with a function, for that class to automatically ensure that mandatory functions are called in part with that function.
I mean, to clarify further:
class domain {
function isEmpty($input) {
//apply conditional logic and results
}
}
class test extends domain {
function addTestToDBTable($test) {
/**
* try to add but this class automatically makes it so that all rules of
* class domain must be passed before it can run
* - so essentially, I am no longer required to call those tests for each and
* every method
**/
}
}
Apologies if this appears incoherent by any means. Sure, it seems lazy but I want to be able to force context without having to concern abou
Update:
Okay, to clarify further: in PHP, if I extend and declare a __construct() for a child class, that child class will override the parent __construct(). I do not want this, I want the parent construct to remain and mandate whatever as it pleases just as the child class may do so also.
I guess it can be done in two different ways.
Aspect Oriented Programming
Have a look here https://github.com/AOP-PHP/AOP
Generate or write Proxy classes
A really simple example could be:
<?php
class A {
public function callMe() {
echo __METHOD__ . "\n";
}
}
class B extends A {
// prevents instantiation
public function __construct() {
}
public function shouldCallMe() {
echo __METHOD__ . "\n";
}
public static function newInstance() {
return new ABProxy();
}
}
class ABProxy {
private $b;
public function __construct() {
$this->b = new B();
}
public function __call($method, $args) {
$this->b->callMe();
return call_user_func_array(array($this->b, $method), $args);
}
}
// make the call
$b = B::newInstance();
$b->shouldCallMe();
// Outputs
// ------------------
// A::callMe
// B::shouldCallMe
Hopes this helps a bit.
Sounds like you want a Decorator.
See This answer for a detailed explanation on how to do it. Note that it does not require a class extension.
I would use a domain-validating decorator with some doc-block metaprogramming magic. But this is really a job for an entire library, which no doubt exists.
fiddle
<?php
class FooDomain {
public static function is_not_empty($input) {
return !empty($input);
}
}
class Foo {
/**
* #domain FooDomain::is_not_empty my_string
*/
public function print_string($my_string) {
echo $my_string . PHP_EOL;
}
}
$foo = new DomainValidator(new Foo());
$foo->print_string('Hello, world!');
try {
$foo->print_string(''); // throws a DomainException
} catch (\DomainException $e) {
echo 'Could not print an empty string...' . PHP_EOL;
}
// ---
class DomainValidator {
const DOMAIN_TAG = '#domain';
private $object;
public function __construct($object) {
$this->object = $object;
}
public function __call($function, $arguments) {
if (!$this->verify_domain($function, $arguments)) {
throw new \DomainException('Bad domain!');
}
return call_user_func_array(
array($this->object, $function),
$arguments
);
}
public function __get($name) {
return $this->object->name;
}
public function __set($name, $value) {
$this->object->name = $value;
}
private function verify_domain($function, $arguments) {
// Get reference to method
$method = new \ReflectionMethod($this->object, $function);
$domains = $this->get_domains($method->getDocComment());
$arguments = $this->parse_arguments(
$method->getParameters(),
$arguments
);
foreach ($domains as $domain) {
if (!call_user_func(
$domain['name'],
$arguments[$domain['parameter']]
)) {
return false;
}
}
return true;
}
private function get_domains($doc_block) {
$lines = explode("\n", $doc_block);
$domains = array();
$domain_tag = DomainValidator::DOMAIN_TAG . ' ';
foreach ($lines as $line) {
$has_domain = stristr($line, $domain_tag) !== false;
if ($has_domain) {
$domain_info = explode($domain_tag, $line);
$domain_info = explode(' ', $domain_info[1]);
$domains[] = array(
'name' => $domain_info[0],
'parameter' => $domain_info[1],
);
}
}
return $domains;
}
private function parse_arguments($parameters, $values) {
$ret = array();
for ($i = 0, $size = sizeof($values); $i < $size; $i++) {
$ret[$parameters[$i]->name] = $values[$i];
}
return $ret;
}
}
Output:
Hello, world!
Could not print an empty string...
im trying to create a class to manage widgets. I have problems with a protected data in parent class:
Widget.php
/** Parent class **/
class Widget{
protected $html =""; //formated html data
// method to load views in {system_path}/widgets/{widget_name}/views/
protected function LoadView($filename){
if(!empty($filename) && is_string($filename)){
$output = "";
$dir = WIDGET_PATH . "views" . DS . $filename;
ob_start();
include($dir);
$output = ob_get_contents();
ob_end_clean();
return $output;
}
return NULL;
}
//method to render formated html data
public function Render(){
if(isset($this->html) && !empty($this->html)){
return $this->html;
}
return NULL;
}
//static method to load a Widget
public static function Load($widgetName){
if(!empty($widgetName) && is_string($widgetName)){
$widgetName = strtolower($widgetName);
if(file_exists(WIDGET_PATH . $widgetName . DS . $widgetName . ".php")){
include_once(WIDGET_PATH . $widgetName . DS . $widgetName . ".php");
if(class_exists($widgetName."_Widget")){
$class = $widgetName."_Widget";
return new $class();
}
}
}
return FALSE;
}
}
/widgets/socialbar.php
/** SocialBar Widget **/
class Socialbar_Widget extends Widget
{
public function __construct(){
$this->html = "demo"; // test to see if it works
}
}
index.php
/*load class files, etc */
$Social = Widget::Load("socialbar"); //works, perfectly loads Socialbar_Widget()
var_dump($social); // works : object(Socialbar_Widget)[29] protected html = 'demo' ......
$Social->Render(); // throws Fatal error: Using $this when not in object context
To extend a variable inside parent class should i use "public"? Or what i mistake.
Thanks for help guys.
Your class name is class Socialbar_Widget,
Your are calling it in lower case
$Social = Widget::Load("socialbar")
and in load method you are doing strtolower($widgetName).
Check class file name.php. Load function may have returning false.
For some user-level debugging I would like to know from which variable a certain property from a class was called. All classes are stdClass, so I can't look for an answer in that direction.
Let's say I have a class Person with properties Name and Sex. The setup looks like $oPerson = new Person(). When I call $oPerson->FirstName = 'Jack'; I would like to figure out the call was made from $oPerson. Can this be achieved?
This isn't perfect (see here for my inspiration and why it's not), but it's a start:
class Person {
var $name;
var $sex;
function Person() {
$this->name = "Jon";
$this->sex = "male";
}
function __get($name) {
$var_name = "";
foreach ($GLOBALS as $key => $val) {
if ($val === $this) {
$var_name = $key;
break;
}
}
echo "Getting undefined '$name' from '\$$var_name'.\n";
$trace = debug_backtrace();
trigger_error(
'Undefined property via __get(): ' . $name .
' in ' . $trace[0]['file'] .
' on line ' . $trace[0]['line'],
E_USER_NOTICE);
return null;
}
}
$oPerson = new Person();
$name = $oPerson->name;
$fullname = $oPerson->fullname;
For example, the following won't work:
class Nerd {
var $person;
function Nerd() {
$person = new Person();
$nerd_fullname = $person->fullname;
}
}
because the $person property of Nerd is not global, so it doesn't appear in $GLOBALS, nor is it within the scope of Persons's magic function __get().