I am new to php and trying using Factory Method.
I want to instantiate my class Shapes using 'build' function,such that when the class 'Circle' is instantiated its area and circumference is calculated and if the class 'Rectangle' is called ,its area and circumference is calculated and returned.
This is what I have so far and what i want to achieve.This doesnt work and i am sure there is a better way of doing it!
$allmyshapes = Shapes::build($initData);
foreach($allmyshapes as $value) {
if ($value =='Circle'){
$x = new Circle();
echo $x::area();
echo $x::circumference();
}
if ($value =='Rectangle'){
$y = new Rectangle();
echo $y::area();
echo $y::circumference();
}
}
My Class:
class Shapes {
public static function build($initData){
}
foreach($newlist as $value){
if($value[0]=='Circle'){
$shape1 =new Circle($value[1],$value[2]);
}
if($value[0]== 'Rectangle'){
$shape2 =new Rectangle($value[1],$value[2]);
}
}
return array($shape1,$shape2);
}
}
class Circle extends Shapes {
public $radius;
public $centre_point;
public function __construct($radius, $centre_point) {
$this->radius = $radius;
$this->centre_point = $centre_point;
}
public function area(){
return (pi() *$this->radius * $this->radius);
}
public function circumference(){
return 2 * pi() *$this->radius;
}
}
class Rectangle extends Shapes {
public $x;
public $y;
public function __construct($x, $y) {
$this->x= $x;
$this->y = $y;
}
public function area() {
return $this->x * $this->y;
}
public function circumference() {
return 2 * ($this->x+ $this->y);
}
}
There's nothing wrong with your classes, it was how you were using them to output information:
class Shapes {
public static function build($initData){
//$initdata is a heredoc
$newlist = explode("\n", $initData);
foreach($newlist as $key => $initData){
$newlist[$key] = explode("\t", $initData);
}
foreach($newlist as $value){
if($value[0] == 'Circle'){
$shape1 = new Circle($value[1], $value[2]);
}
if($value[0] == 'Rectangle'){
$shape2 = new Rectangle($value[1], $value[2]);
}
}
return array($shape1, $shape2);
}
}
class Circle extends Shapes {
public $radius;
public $centre_point;
public function __construct($radius, $centre_point){
$this->radius = $radius;
$this->centre_point = $centre_point;
}
public function area(){
return (pi() * $this->radius * $this->radius);
}
public function circumference(){
return 2 * pi() * $this->radius;
}
public function draw(){
return ( $this->radius.":".$this->centre_point);
}
}
class Rectangle extends Shapes {
public $x;
public $y;
public function __construct($x, $y){
$this->x = $x;
$this->y = $y;
}
public function area(){
return $this->x * $this->y;
}
public function circumference(){
return 2 * ($this->x + $this->y);
}
}
$initData = <<<ENDINIT
Circle 5 2
Rectangle 5 10
Ellipse 10 10 4 5
ENDINIT;
$allmyshapes = Shapes::build($initData);
foreach($allmyshapes as $value){
if(get_class($value) === 'Circle'){
echo nl2br("**Circle**\nArea: ".$value->area()."\nCircumference: ".$value->circumference()."\n");
}
if(get_class($value) === 'Rectangle'){
echo nl2br("**Rectangle**\nArea: ".$value->area()."\nCircumference: ".$value->circumference()."\n");
}
}
I added 2 functions to your provided code, get_class() & nl2br(). the nl2br can be removed if you are using this in CLI format, but its main function was for output readability when using a web browser to view the output.
get_class() will solve your issue of trying to figure out what constructor was used in the creation of the object.
Related
I want my PHP IDE (NuSphere PhpEd) to detect a property of my 2D array element ( an object ) whose property is not showing up after I type a right arrow in my IDE.
Is there any way in PHP 7 to auto generate suggestions of a multidimensional array element properties where each element is an object with certain properties?
<?php
class Cell
{
private $color;
public function __construct()
{
$this->color = "red";
}
public function __get($propertyName)
{
if ($propertyName == 'color')
return $this->color;
}
public function __set($propertyName, $value)
{
if ($propertyName == 'color')
$this->color = $value;
}
}
class Matrix implements ArrayAccess
{
private $matrix = array();
public function __construct($maxX, $maxY)
{
$this->matrix = array_fill(1, $maxX, array_fill(1, $maxY, null));
}
public function &offsetGet($name)
{
return $this->matrix[$name];
}
public function offsetSet($name, $value)
{
$this->matrix[$name] = $value;
}
public function offsetExists($name)
{
return isset($this->matrix[$name]);
}
public function offsetUnset($name)
{
unset($this->matrix[$name]);
}
}
$matrix = new Matrix(3,3);
for ($xIdx = 1; $xIdx <= 3; $xIdx++)
for ($yIdx = 1; $yIdx <= 3; $yIdx++)
$matrix[$xIdx][$yIdx] = new Cell();
$matrix[2][2]->color = "green";
echo $matrix[2][2]->color;
?>
If you're happy to use phpdoc annotations, you can use the Type[][] annotation to declare a variable as being a 2D array of Type. In the case of a class property that looks like:
/** #var Cell[][] */
private $matrix = [];
Or for the return value of a class method:
/**
* #return Cell[][]
*/
public function getMatrix() {
return $this->matrix;
}
In the case of PHPStorm, that provides this:
I tried a workaround in order to force phpdoc to pick up my property or method following the arrow.
Here is what I did.
class Matrix
{
protected $maxX;
protected $maxY;
private $matrix = array();
public function __construct($maxX, $maxY)
{
$this->maxX = $maxX;
$this->maxY = $maxY;
$this->matrix = array_fill(1, $maxX, array_fill(1, $maxY, 0));
return $this;
}
public function getMaxX()
{
return $this->maxX;
}
public function getMaxY()
{
return $this->maxY;
}
public function get($x, $y)
{
if (isset($this->matrix[$x][$y]))
return $this->matrix[$x][$y];
throw new OutOfBoundsException("Array at indices $x, $y is out of bounds!");
}
}
class Main
{
public function __construct()
{
}
public function setArrayVal($x, $y)
{
$cell = new Cell();
//Set Value in some arbitrary method in Main Class
$this->matrix($x, $y)->set($cell);
//Or if $val is public in Cell class
// $this->matrix($x, $y)->val = $cell;
}
//Method of Main Class
public function matrix($x, $y) : Cell //Note Cell here specified for type hinting
{
//Note, matrix below is a property, not method, whose type corresponds to the matrix class
return $this->matrix->set($x, $y);
}
}
class Cell
{
private $val;
public function __construct()
{
}
// Method set in Cell class
public function set($val)
{
$this->val = $val;
}
}
I am trying to convert some classes from Ruby to PHP for a project I am working on. I think I almost have it, but I am unversed in Ruby so I am struggling with understanding some aspects and what the equivalencies would be in PHP.
So the Ruby class is as follows:
class Log
def initialize (x,y,list,url)
#line = 0
#x=x
#y=y
#url=url
#list=list
#points = Hash.new(0)
#list.each do |point|
#points[point.xy] +=1
end
#reps = #points.values.max
end
attr_reader :x, :y, :list, :reps, :url
def next
coord = #list[#line]
#line += 1
return coord
end
end
Here is what I have written in PHP thus far: (I also added a note for what the original is supposed to be doing)
<?php
/*
* Stores all the values pertinent to a single URL and gives accessors to them.
* There’s also a “next” method that returns next click within the same URL
*/
class Log
{
private $x;
private $y;
private $url;
private $list;
private $reps;
private $points;
function __construct($x,$y,$list,$url)
{
$this->line = 0;
$this->x = $x;
$this->y = $y;
$this->url = $url;
$this->list = $list;
$this->points = array();
foreach ($list as $l_attr => $l_val) {
if($l_attr == 'xy'){
$this->points[$l_val];
}
}
$this->reps = count($this->points);
return $this;
}
function next(){
$coord = next($this->list);
return $coord;
}
public function __get($property) {
if (property_exists($this, $property)) {
return $this->$property;
}
}
}
I am trying to keep everything OOP to match the rest of my project.
I would really just like to know if I am doing this right or if I am way off. ;)
I'm not good at ruby so I have a hard time understanding the code but this should be pretty accurate:
<?php
class Log {
private $line = 0;
private $x;
private $y;
private $url;
private $list;
private $reps;
public function __construct($x, $y, $url, $list)
{
$this->x = $x;
$this->y = $y;
$this->url = $url;
$this->list = $list;
$points = [];
foreach ($list as $point) {
$points[$point['xy']] += 1;
}
$this->reps = max($points);
}
public function getX()
{
return $this->x;
}
public function getY()
{
return $this->y;
}
public function getUrl()
{
return $this->url;
}
public function getList()
{
return $this->list;
}
public function getReps()
{
return $this->reps;
}
public function next()
{
$coord = $this->list[$this->line];
$this->line += 1;
return $coord;
}
}
I'm not sure about the $points[$point['xy']] += 1; part as it doesn't really make sense to me so be sure to fix that as you see fit. Also, it is recommended to define setters manually instead of using the magic methods like __get().
class Point
{
private $x, $y;
public __construction ($x, $y)
{
$this->x = $x;
$this->y = $y;
}
public function getX()
{
return $this->x;
}
public function getY()
{
return $this->y;
}
}
first I would write this:
class Item
{
private $point; // Point
public __construction()
{
$this->point = new Point(0,0);
}
public function getPoint()
{
return $this->point;
}
}
and then:
$p = new Item();
$p->getPoint()->getX();
but they say it violates that law. After refactoring:
class Item
{
private $point; // Point
public __construction()
{
$this->point = new Point(0,0);
}
public function getPointX()
{
return $this->point->getX();
}
public function getPointY()
{
return $this->point->getY();
}
}
and then:
$p = new Item();
$p->getPointX();
this time getPointX() and getPointY() is just a redundant "transmission" method. I understand that ig Point has 1000 other methods, it would be unsafe to just return this all object as return $this->point. But this time all properties are covered.
Source
Because your $p requires more information about Point than it needs:
In particular, an object should avoid invoking methods of a member object returned by another method
For example consider class X that has some utility methods ('foo','bar') that do some operation on some property of X. These method are also useful for other external variable.
Some may implement X and staticX classes as below:class Foo
class StaticX
{
public static function foo($p)
{
return $p * $p;
}
}
class X
{
private $p=4;
public function foo()
{
return StaticX::foo($this->p);
}
}
$x= new x;
echo $x->foo();
echo StaticX::foo(3);
But this approach has some maintainability issues.
Is there any better solution?
class X
{
private $p;
public function foo()
{
return self::doFoo($this->p);
}
public static function doFoo($p)
{
return $p * $p;
}
}
I like foolishSeths answer, but what about this?
class X
{
private static $p;
public static function foo($p=null)
{
if ( is_null( $p ) ) {
$p = self::$p;
}
return $p * $p;
}
}
Since PHP 5.4 you can make use of traits. See http://www.php.net/manual/en/language.oop5.traits.php
trait fooBehavior {
function getFoo() { return self::foo($this->p); }
static function foo($p) { return $p * $p; }
}
class X {
use fooBehavior;
private $p;
public function __construct($p) { $this->p = $p; }
}
$x = new X(2);
echo $x->getFoo(); // echoes 4
echo $x::foo(2); // echoes 4
Is there a way to iterate over an object's keys implementing ArrayAccess and Iterator interfaces? Array access works as a charm but I can't use foreach on those objects which would help me a lot. Is it possible? I have such code so far:
<?php
class IteratorTest implements ArrayAccess, Iterator {
private $pointer = 0;
public function offsetExists($index) {
return isset($this->objects[$index]);
}
public function offsetGet($index) {
return $this->objects[$index];
}
public function offsetSet($index, $newValue) {
$this->objects[$index] = $newValue;
}
public function offsetUnset($index) {
unset($this->objects[$index]);
}
public function key() {
return $this->pointer;
}
public function current() {
return $this->objects[$this -> pointer];
}
public function next() {
$this->pointer++;
}
public function rewind() {
$this->pointer = 0;
}
public function seek($position) {
$this->pointer = $position;
}
public function valid() {
return isset($this->objects[$this -> pointer]);
}
}
$it = new IteratorTest();
$it['one'] = 1;
$it['two'] = 2;
foreach ($it as $k => $v) {
echo "$k: $v\n";
}
// expected result:
// one: 1
// two: 2
Thanks for any help and hints.
I use this to implement iterator. Maybe you can adapt to your code ;)
class ModelList implements Iterator{
public $list;
private $index = 0;
public $nb;
public $nbTotal;
/**
* list navigation
*/
public function rewind(){$this->index = 0;}
public function current(){$k = array_keys($this->list);$var = $this->list[$k[$this->index]];return $var;}
public function key(){$k = array_keys($this->list);$var = $k[$this->index];return $var;}
public function next(){$k = array_keys($this->list);if (isset($k[++$this->index])) {$var = $this->list[$k[$this->index]];return $var;} else {return false;}}
public function valid(){$k = array_keys($this->list);$var = isset($k[$this->index]);return $var;}
/**
*
* Constructor
*/
public function __construct() {
$this->list = array();
$this->nb = 0;
$this->nbTotal = 0;
return $this;
}
}
while ($it->valid()) {
echo $it->key().' '.$it->current();
$it->next();
}
Would be my approach, however, this function looks iffy:
public function next() {
$this->pointer++;
}
Incrementing 'one' isn't likely to give you 'two'. Try the code in the answers to this question to get the next array key:
$keys = array_keys($this->objects);
$position = array_search($this->key(), $keys);
if (isset($keys[$position + 1])) {
$this->pointer = $keys[$position + 1];
} else {
$this->pointer = false;
}