Can I get an example please? - php

$starcraft = array(
"drone" => array( "cost" => "6_0-",
"gas" => "192",
"minerals" => "33",
"attack" => "123",
)
"zealot" => array( "cost" => "5_0-",
"gas" => "112",
"minerals" => "21",
"attack" => "321",
)
)
I'm playing with oop and I want to display the information in this array using a class, but I don't know how to construct the class to display it.
This is what I have so far, and I don't know where to go from here. Am I supposed to use setters and getters?
class gamesInfo($game) {
$unitname;
$cost;
$gas;
$minerals;
$attack;
}

You're actually pretty close so far.
In OOP, each object usually represents a discrete concept. Therefore, a better name for your class would be Unit because it represents a unit in the game. Example:
class Unit {
var $name;
var $cost;
var $gas;
var $minerals;
var $attack;
function setName($name) {
$this->name = $name;
}
function getName() {
return $this->name;
}
}
Note that this example has the appropriate instance variables (name, cost, etc) and a getter/setter pair for the name variable. You'd want to add more getter/setter pairs for the other instance variables.
Once you have all your getter/setters, you can instantiate a Unit by doing this:
$zealot = new Unit();
$zealot->setName("Zealot");
$zealot->setAttack(321);
... etc.
You'll also want to learn about constructors, so that you can instantiate a Unit this way:
$zealot = new Unit("Zealot");
$zealot->setAttack(321);
You can see that a constructor would give you a bit of a shortcut there by letting you set the unit name at the same time you instantiate the Unit class.
So to print a Unit object, you'd do something like:
echo $zealot->getName();
Edit: Like zerkms said, OOP is complex, and what I described here is basically programming with classes and objects. This is just the very beginning of OOP.

Why did not you start with http://ru.php.net/manual/en/language.oop5.basic.php?

Related

Dynamic contructor values for unit test in php

I am currently in the process of doing some refactoring. The stuff is not done by me. I just have to deal with it.
$expected = new instance(0,0,Argument::any());
$result = $this->otherInstance->returnsInstance([]);
$this->assertEquals($expected, $result);
Instance is some kind of model, which is returned by otherInstance. The problem is that the third argument is dynamic and an integer. It can be anything. As you can see, it is mandatory for instantiation of the model. Can this be mocked somehow? How do I set up the test properly?
This does obviously not work ...
::__construct() must be of the type integer, object given
So, how do I mock this? Or how do I set up the test in such a way as to handle dynamic values? The language level is 7.1, but I want to move to 7.4 soon.
One way to work with different test scenarios is using Data Providers.
In the example below, you set some arbitrary values in the method provideModelConstructorArguments. The test testMyTest will run twice, one time for each set of values present in the data provider.
public function provideModelConstructorArguments()
{
return [
[
'argument1' => 0,
'argument2' => 1,
'argument3' => 100
],
[
'argument1' => 5,
'argument2' => 3,
'argument3' => 57
]
];
}
/**
* #dataProvider provideModelConstructorArguments
**/
public function testMyTest($argument1, $argument2, $argument3)
{
$expected = new instance($argument1, $argument2, $argument3);
//continue your test code
}

How to write OOP in PHP

Can anybody help is this code can be called OOP design. How to Create a class in PHP which shows that is OOP design.
I'm trying to create a simple example class before I start implementing the following into my projects and I would like to know if there is something I could/should improve. I will be very glad if someone gives me some input of how am I doing now.
This is simple class which is sorting associative array columns. Can anybody write simple class regarding sorting associative array. It can be anything just should follow OOP design pattern.
Thanks in advance for help.
<?php
interface PassangerBoardingInterface
{
public function ArrageBoardingTicket(string $from, string $to, string $instruction) ;
public function GetEmbededHTMlTemplate ( $template, $regrex);
}
class PassengerBoardingSorterOOP implements PassangerBoardingInterface
{
// Boarding list
public $boardingList;
// Journey start message
public $startMessage;
// End message
public $endMessage;
// Get Template
public $getTemplate;
public function __construct(array $boardingList )
{
$this->boardingList = $boardingList;
}
public function ArrageBoardingTicket(string $from, string $to , string $instruction)
{
foreach($this->boardingList as $boardingList)
{
usort($this->boardingList, function ($a, $b) use ($to, $from){
return ($a[$to] === $b[$from] ) ? 0 : 1;
});
}
// Defining index
$i = 0;
$coutboarding = count($this->boardingList);
// Appending data to start index
$this->boardingList[0][$instruction] = $this->startMessage;
// Appending string to end column
$this->boardingList[$coutboarding - 1 ][$instruction] = $this->endMessage;
}
public function GetEmbededHTMlTemplate($template, $regrex)
{
$result = '';
/* Loop each data */
for($j = 0; $j < count($this->boardingList); $j++)
{
/* Get the template */
$output = $template;
/* where indexs matches in template */
foreach($this->boardingList[$j] as $key => $value)
{
$reg = str_replace('(.*?)',$key, $regrex);
/* Check with regular expression */
if(preg_match($reg,$template))
{
/* Replace with */
$output = preg_replace($reg,$value,$output);
}
}
$result .= $output;
}
$this->getTemplate = $result;
}
}
==============================
I am using below structure
//My array of information
$cards = [
[
"from" => "Barcelona",
"to" => "New York",
"instruction" => "",
'time' => '2018-02-02 20:05',
'transport' => 'Flight' ,
'transportno' => 'B33',
'seatno' => 'Y15'
],
[
"from"=> "Barcelona",
"to" => "Gerona",
"instruction" => "",
'time' => '2018-02-02 20:05',
'transport' => 'Bus' ,
'transportno' => 'M31, M32, M33','seatno' => 'Any'
], // 1
[
"from" => "Madrid",
"to" => "Barcelona",
"instruction" => "",
'time' => '2018-02-02 20:05',
'transport' => 'Bus' ,
'transportno' => 'M31, M32, M33',
'seatno' => 'Any'
],
["from" => "New York",
"to" => "Stockholm",
"instruction" => "",
'time' => '2018-02-02 20:05', 'transport' => 'Flight' ,
'transportno' => 'M31, M32, M33','seatno' => 'Any'
], // 0
[
"from" => "Gerona",
"to" => "Barcelona",
"instruction" => "",
'time' => '2018-02-02 20:05',
'transport' => 'Bus' ,
'transportno' => 'M31, M32, M33',
'seatno' => 'Any'
], // 2
];
$obj = new PassengerBoardingSorterOOP($cards);
$obj->startMessage = 'Your journey start from here ';
// End message
$obj->endMessage = 'Your journey ends here';
$obj->ArrageBoardingTicket('from', 'to', 'instruction');
// Form a template so you can get template list
$template = '<li class="list-group-item">{{&instruction}}Take {{&transport}} From {{&from}} To {{&to}},
{{&transport}} No {{&transportno}}, {{&transport}} Departure Date {{&time}}. Seat Number {{&seatno}}.</li>';
$regrex = '/{{&(.*?)}}/';
$obj->GetEmbededHTMlTemplate($template, $regrex);
echo $obj->getTemplate;
The code you provided violates a few rules for OOP hence can not be considered as such.
It is a representation of objects that interact with each other in order to create your program, instead of scripts that call each other (hope this makes sense).
You have to start thinking how you can represent your problem into objects.
Let's say you have a car to represent in PHP.
You will start to think you can have:
A car class;
A Wheel class (you car can have 4 wheels);
Then you think "But a car is a vehicle and, for example, a bike only have 2 wheels" and then you create your "Vehicle class";
Then you enhance your Car Class and extend it from Vehicle;
Now, each Vehicle have tyres, but you do not specify how many, that is for each type to decide, not the parent class that "abstracts them".
And so on. You quickly see here that to tackle this problem (and we are on the very start) you already have;
Abstract Class Vehicle;
Car Class (that extends Vehicle);
Bike Class (that extends Vehicle);
Wheel Class (That can be added to Vehicle types -> Your Car and your Bike);
When you want to move to OOP there are a few rules you need to consider:
Encapsulation;
Polymorphism;
Open/Close implementation;
Inheritance;
Open Recursion;
You want to restrict your Class objects to only understand "what they are". So a Car does not hold logic for a Bike and vice-versa.
1. Encapsulation -> This prevents external code from manipulating or "holding concern" to what happens on the target class internally;
It refers to the use of private and protected methods;
How the Car class behaves and what it triggers to work in whatever logic you decide a Car should work, is completely hidden for the other classes like Bike. They do not need to know what clicks on each other!
2. Polymorphism -> For example, the number of tyres, or even the Tyre types for each Vehicle, it will be different. Therefore you can declare the Vehicle can addTyres BUT each Vehicle type (like Car and Bike) will implement how to addTyres differently (You can limit bikes to have 2 tyres only whilst cars have 4);
3. Open/Close implementation -> When you create classes you should think on them as they should be OPEN for extension BUT CLOSED for modification. This means you can extend their behaviour BUT you should never have to go back and change your object behaviour;
4. Inheritance -> Allows your Classes to implement "hierarchy". Think of it as "parent" and "children". What came "First" and what came "after". Like a Shape is a parent of a Circle. If you announcing your argument, Shape is very abstract as it represents anything from Squares, Triangles, Circles, etc. But if you refer to a Square you know about it in more detail, like vertices, angles, etc! Shapes represent that but refine nothing towards what type, just what structure should be but not "how it will be";
5. Open Recursion -> Classes are able to call each other and even other classes. That is how they interact. Hell they can even have other objects as part of their attributes;
In a very resumed source this is a very BRIEF introduction to OOP!
Once you get the hang of it it is amazing and you will have such a powerful logical code and so organised I swear to you, you will not go back :3
But yeah, this is not enough to even begin telling you OOP! :x Just think of it this way, it is a walk, not a destination :D
PS: A great way to start getting used to OOP is also starting to look at Design Patterns

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.

How to show dirty values when debugging entity

I need use _get so I just did it at User entity just for test:
protected function _getName($name)
{
return $name . ' - FOOBAR';
}
So in the view I did Debug($user), and heres the result:
'properties' => [
'id' => (int) 32,
'name' => 'Daniel Pedro', //<- Clean Value
'email' => 'daniel#gmail.com',
],
'dirty' => [],
'original' => [],
'virtual' => [],
'errors' => [],
As you can notice the property name is with the original value Daniel Pedro, so I thought I did something wrong at _getName but when I look at the input at form the value was Daniel Pedro - FOOBAR.
My question is, how can I show the mutated values at Debug?
Debug the values separately
The most simple way to check the properties with their possible muatated values, is by extracting the visible properties, something like
debug($entity->extract($entity->visibleProperties()));
This won't include the ones that have been defined as "hidden" in the $_hidden property, if you need them too, then you'll have to explicitly include them
debug($entity->extract(array_merge($entity->visibleProperties(), $entity->hidden())));
Extend the debug info
If you'd wanted to somehow include this in the debug output of entities in general, then you'll have to overwrite the EntityTrait::__debugInfo() method and add the mutated properties in there.
Simple example, in your entity class (you can create a base entity class that all your entites extend so that you have this functionality in all entities):
public function __debugInfo()
{
$info = parent::__debugInfo();
$info['propertiesIncludingPossiblyMutatedValues'] =
$this->extract(array_keys($this->_properties));
return $info;
}
Or if you'd wanted to see only the ones that have really been mutated:
public function __debugInfo()
{
$info = parent::__debugInfo();
$info['mutated'] = array_diff(
$this->extract(array_keys($this->_properties)),
$this->_properties
);
return $info;
}
This should give you a hint of how things work.

Setting Properties via Methods

I am new to OOP.
I am currently working on adding data to an object to then submit it to a database. I have created a method called setData to take two arguments, and then add those arguments into the object.
Inside my class, I use this
public function setData($value1, $value2) {
$this->$value1 = $value2;
}
Where on a form submit, that function is used to store data
$sites = new AddWebsites;
$sites->setData('name', $name);
$sites->setData('page_rank', $pr);
$sites->setData('pa', $pa);
$sites->setData('da', $da);
$sites->setData('tf', $tf);
$sites->setData('cf', $cf);
$sites->setData('keywords', $keywords);
$sites->setData('notes', $notes);
This will output the following data
AddWebsites Object
(
[name] => asdf.com
[page_rank] => 5
[pa] => 15
[da] => 25
[tf] => 14
[cf] => 62
[keywords] => Array
(
[0] => kw1
[1] => kw2
[2] => kw3
[3] => kw4
[4] => kw5
)
[notes] => asdf
)
I have been told that this is wrong, and will throw errors.
I was wondering if there is a better way to achieve this, if it is actually wrong, and if there is an easier way to do this.
With error reporting enabled, I have not run across anything that tells me what I am doing is wrong.
Thanks for your time.
It's wrong in pure OOP terms because you're using PHP's (somewhat unusual) ability to add arbitrary attributes to instantiated objects via your setData method.
What you should be doing - to achieve the goals of encapsulation and data validation - is something like this :
class AddWebsites {
private $name;
private $pageRank;
// etc
// Setters
public function setName(value) {
// you can put validation logic in here
this->name = value;
}
public function setPageRank(value) {
// you can put validation logic in here
this->pageRank = value;
}
// etc
// getters
public function getName() {
return this->name;
}
public function getPageRank() {
return this->pageRank;
}
}
This is using "Getters" and "Setters".
You could however have your members as public then you wouldn't need the getters
One of things i can notice is passing field name in function parameter is not an good idea. Reason behind that is if you by mistake pass wrong field name then php will create one more field for that object.
So if you are having multiple objects of same class some will have that field some will not. This leads to inconsistency.
So I feel this is not correct thing to do as you are not suppose to create properties of class pbject dynamically.
Ideal way is to have different getter and setter functions for each field and fields should be private in scope, so that you/developer will not not able to create new fields by mistake.

Categories