The clean code says, that is not recommended to initialize objects, use if statement or other things in the __constructor.
I have a class in which I used elements in the constructor which are not allowed.
How to rebuild it to conform to the rules?
I searched on google! But I do not really understand and I hope that I will succeed in understanding with this particular example.
The full code is also available on github: https://github.com/KoreLewi/21-blackjack-game
public function __construct(array $Cards = array())
{
if (empty($Cards)) {
$Cards = $this->initEnglishDeck();
}
parent::__construct($Cards);
}
public function initEnglishDeck()
{
$arrCards = array();
foreach ($this->suits as $suit) {
foreach ($this->cards as $card) {
$arrCards[] = new Card($suit, $card);
}
}
return $arrCards;
}
The full code is also available on github: https://github.com/KoreLewi/21-blackjack-game
The pattern is Dependency Injection rather than initialising your dependencies internaly.
One way to "fix" your code is to have a CardDeck Interface and multiple (or just in this case) a EnglishDeck class which implements CardDeck.
And in all the classes which require a card deck you inject it in the constructor like this:
class Game {
/**
* #var CardDeck
*/
private $cardDeck
public function __construct(CardDeck $cardDeck) {
$this->cardDeck = $cardDeck
}
}
In that case your Game class would still work even if you decide it to pass him another type of CardDeck, e.g. FrenchDeck which would also implement the CardDeck interface.
You could:
Move the if statement into the initEnglishDeck() function.
public function __construct(array $Cards = array()) {
$Cards = $this->initEnglishDeck($Cards);
parent::__construct($Cards);
}
public function initEnglishDeck($cards = array()) {
if(!empty($cards)) return $cards;
$arrCards = array();
foreach ($this->suits as $suit) {
foreach ($this->cards as $card) {
$arrCards[] = new Card($suit, $card);
}
}
return $arrCards;
}
Related
I'm self-studying the PHP language. And I'm focused on the latest PHP OOP language.
I search for some "ready-to-install" PHP software and as I scan for some references to search and know, I saw lines of code with a structure like this (can't remember so I'll create my own):
$myapp->settings->getValue('openforum');
$myapp->settings->setValue('closeformaintenance', '1');
So my question is, how can I reproduce the code above? I don't know what term to use that line of code (objects, I guess?).
Something like this:
$newLogin->search($uid)->setLogin($dateToday);
Like that. I really need to do that way so I can organize my coding structure. Thanks by the way.
And also for the final question, IS THAT POSSIBLE?
Here's a fairly straight forward way of looking at it, using dependency injection.
Try it out: https://3v4l.org/iSJgL
Note, the below requires PHP 7 due to the string type hint. Remove that and I believe it should work in 5.6 just fine.
<?php
$myapp = new MyApp(new SettingsBag([
'works' => false,
'random' => rand(),
]));
var_dump($myapp->settings()->get('random'));
var_dump($myapp->settings()->get('works'));
// Let's change it up...
$myapp->settings()->set('works', true);
// Now it should be true.
var_dump($myapp->settings()->get('works'));
These would normally have namespaces like \App and/or \App\Configuration, but I ignore that here so it's easier to follow:
class MyApp {
private $settings_bag = null;
function __construct(SettingsBag $settings_bag)
{
$this->settings_bag = $settings_bag;
}
public function settings()
{
return $this->settings_bag;
}
}
class SettingsBag {
private $settings = null;
function __construct(array $settings = [])
{
$this->settings = $settings;
}
public function set(string $key, $value)
{
return $this->settings[$key] = $value;
}
public function get(string $key)
{
return $this->settings[$key];
}
}
What you try to achieve is called method chaining. You can get this by the following:
<?php
class TestClass {
private $val = '';
public function test1($val) {
$this->val = $val;
return $this;
}
public function test2() {
echo 'Hello '.$this->val;
}
}
$test->test1('World')->test2(); // Hello World
You have simply to return the instance of the object on the method to allow the method chaining.
You can read more here.
It's method chaining.
See code below:
class T {
public function test() {
// do something
return $this;
}
}
$x = new T;
$x->test()->test();
I'm developing an Apigility driven application based on the Zend Framework 2.
Currently I'm sending the data retrieved in the database directly to the client: a request comes in, the MyResource#fetch(...) or MyResource#fetchAll(...) gets triggered and calls an appropriate method on MyService class, that calls MyMapper to retireve the data with its methods like findFooByBar(...).
Now I'd like to process the data, before the response is sent. How can I do that?
The Apigility ZF HAL documentation shows, how to access the entity data between it has been retrieved and sent to the client. Well I tried this out. It's ugly and to much code for such task. And... it doesn't work. I want however post here my attept:
namespace Portfolio;
...
class Module implements ApigilityProviderInterface {
private $serviceManager;
public function onBootstrap(MvcEvent $event) {
$application = $event->getTarget();
$this->serviceManager = $serviceManager = $application->getServiceManager();
$viewHelperManager = $serviceManager->get('ViewHelperManager');
$hal = $viewHelperManager->get('Hal');
$hal->getEventManager()->attach('renderEntity', array($this, 'onRenderEntity'));
$hal->getEventManager()->attach('renderCollection', array($this, 'onRenderCollection'));
}
public function onRenderEntity($event) {
$entity = $event->getParam('entity');
if ($entity->entity instanceof ProjectEntity) {
$projectEntity = $entity->entity;
$imageCollection = $this->tempCreateimagesForProject(
$event, $entity->entity->getId()
);
$projectEntity->setImages($imageCollection);
$event->setParam('entity', $projectEntity);
}
}
public function onRenderCollection($event) {
$collection = $event->getParam('collection');
$projectCollection = $collection->getCollection();
if ($projectCollection instanceof ProjectCollection) {
foreach ($projectCollection as $key => $projectItem) {
$tempProject = $projectCollection->getItem($key);
$tempProject->append(
['images' => $this->tempCreateimagesForProject($tempProject->offsetGet('id'))]
);
$projectCollection->getItem($key)->offsetSet($key, $tempProject);
}
}
}
private function tempCreateimagesForProject(Event $event, $projectId) {
$imageService = $this->serviceManager->get('Portfolio\V2\Rest\ImageService');
$imageCollection = $imageService->getImagesForProject($projectId);
return $imageCollection;
}
...
}
I think using the renderEntity and renderCollection events is not the correct spot to add this kind of resource specific logic. It is more suitable for more general changes or incidental customization.
You can add this logic to your resource listeners. So in your fetch and fetchAll methods in your MyResource class you can add the custom code you currently added in these onRenderEntity and onRenderCollection methods.
So something like this:
class MyResource extends AbstractResourceListener
{
/**
* Your image service dependency
*/
protected $imageService;
/* ... */
public function fetch($id)
{
$project = $this->projectMapper->fetch($id);
$imageCollection = $this->imageService->getImagesForProject($project);
$project->setImages($imageCollection);
return $project;
}
/* ... */
public function fetchAll($params = array())
{
$projects = $this->projectMapper->fetchAll();
foreach ($projects as $key => $project) {
$imageCollection = $this->imageService->getImagesForProject($project);
$project->setImages($imageCollection);
}
return $projects;
}
/* ... */
}
One possible solution is handling the data in the Hydrator. So we write a custom Hydrator class and enrich the items with nested objects and lists in it. It can look like this:
Portfolio\V2\Rest\Project\ProjectHydrator
...
class ProjectHydrator extends ClassMethods {
/**
* #var ImageService
*/
protected $imageService;
...
/*
* Doesn't need to be implemented:
* the ClassMethods#hydrate(...) handle the $data already as wished.
*/
/*
public function hydrate(array $data, $object) {
$object = parent::hydrate($data, $object);
if ($object->getId() !== null) {
$images = $this->imageService->getImagesForProject($object->getId());
$object->setImages($images);
}
return $object;
}
*/
/**
* #see \Zend\Stdlib\Hydrator\ClassMethods::extract()
*/
public function extract($object) {
$array = parent::extract($object);
if ($array['id'] !== null) {
$images = $this->imageService->getImagesForProject($array['id']);
$array['images'] = $images;
}
return $array;
}
}
It's not a nice solution, since then a part of the model / data retrieving logic gets moved to the hydrator. But it works. Here is shown an implementation of this approach and here is a discussion to this topic on GitHub.
If you are using the ClassMethods Hydrator and your Collection extends \Zend\Paginator\Paginator a good solution without losing the Collection's consistency and not changing anybody's code is to overwrite your getCurrentItems() method.
public class MyResourceCollection // most likely extends Paginator
{
public function getCurrentItems()
{
// getting the original items
$items = parent::getCurrentItems();
// as we work with objects $item will be an object
// when working with objects we use references to them not clones of objects
// so changes to $item are also present in the collection
foreach ($collection as $item) {
$oldSomething = $item->getSomething();
$item->setSomething('['.$oldSomething.']');
}
// $items are now changed, return them
return $items;
}
}
I have named the key something not to get confused with the getValue method from other places.
This makes the something value look like [something].
I didn't find similar questions/solutions Googleing, or on Stackoverflow, so here I am, in need for help.
I'm porting a PHP project from a custom framework to the Symfony framework (v2.5) which had it's custom dependency injection (based on class reflection). Many of the business logic classes have injected properties and I'm interested if there's a way to do something similar in Symfony without declaring those classes as services, because some of the classes are used for temporary holding/manipulating content coming from 3rd party endpoints and I need different instances of them.
More precisely is there a way to use Dependency Injection in combination with instantiating objects using the new keyword. So if I needed a new class that's dependent on logger or a validator, how could I do something like this.
Content class that I want to solve dependency injection for:
class Content extends SomeObject
{
private $pictureUrl;
...
public __construct(array $array = null)
{
parent::__construct($array);
}
public setPicture(...\Picture $picture)
{
// what's the best way to inject/enable this into my class
$errors = $this->get('validator')->validate($picture->getUrl());
...
$this->pictureUrl = $picture->getUrl();
}
...
}
The class that instantiates the previous class (in this particular case this could be a service, but there are other cases where this is another business logic related class):
class EndpointService extends Service
{
...
public fetchContent()
{
$dataArray = $this->endpoint->fetch();
$contentArray = array();
foreach ($dataArray as $key => $value) {
$content = new Content(array());
$content->setPicture($value->picture);
...
$contentArray[$key] = $content;
}
}
...
}
Note: If there are logical/syntactical errors please ignore them, this is an example through which I demonstrate my problem, and is a distant abstraction from my code base.
It looks to me that your Content is a model, and that the validation should be done in the EndpointService class.
You might consider refactoring so that the validator can be injected to the Service parent class that EndpointService inherits from, if you don't want to inject it to the EndpointService directly.
class EndpointService extends Service
{
protected $validator;
public __construct(Validator $validator) {
$this->validator = $validator;
}
...
public fetchContent()
{
$dataArray = $this->endpoint->fetch();
$contentArray = array();
foreach ($dataArray as $key => $value) {
$content = new Content(array());
$errors = $this->validator->validate($value->picture->getUrl());
$content->setPicture($value->picture);
...
$contentArray[$key] = $content;
}
}
...
}
And in your config:
// config.yml
services:
app.endpoint_service:
class: Acme\DemoBundle\External\EndpointService
arguments: [#validator]
I see one way you could do it, by passing the validator depedency to your EndpointService through dependency injection. You could then pass it to the new Content.
class EndpointService extends Service
{
...
public function construct($validator)
{
$this->validator = $validator;
}
public fetchContent()
{
$dataArray = $this->endpoint->fetch();
$contentArray = array();
foreach ($dataArray as $key => $value) {
$content = new Content($this->validator, array());
$content->setPicture($value->picture);
...
$contentArray[$key] = $content;
}
}
...
}
class Content extends SomeObject
{
private $pictureUrl;
...
public __construct($validator, array $array = null)
{
$this->validator = $validator;
parent::__construct($array);
}
public setPicture(...\Picture $picture)
{
// what's the best way to inject/enable this into my class
$errors = $this->validator->validate($picture->getUrl());
...
$this->pictureUrl = $picture->getUrl();
}
...
}
I am trying to add functions to class from a separate file, I wonder if this could be possible!
$mClass = new MyClass();
$mClass->new_Functions[0](10); // Is there a way to have it in this form?
class myClass
{
private $Pvar = 5;
$new_Fcuntions;
function __construct()
{
include('additional.functions.php');
$arr = get_defined_functions();
$this->new_Functions = $arr['user'];
// trying to call the function with parameter 10
call_user_func(array($this, $this->new_Functions[0]), 10);
}
}
[additional.functions.php] file
function operate($y)
{
return $this->Pvar * $y;
}
----- Edited ------- as it wasn't clear!
"additional.functions.php" is a module and there will be multiple modules to be added to the application, and every module could have more than single function and modules could call one another!
additional.functions.php [module file]
function operate($y)
{
return $this->Pvar * $y;
}
function do-more($foo)
{
return $this->operate(20) + $foo;
}
another.functions.php [another module]
function do-another($foo)
{
return $this->do-more(30) - $foo;
}
function add($foo, $bar)
{
return $foo + $bar;
}
appreciate every participation, its been a while since I am trying to maneuver around with it!
Is this possible or should I give up!
It looks to me like you are looking for Traits, which are a new feature as of PHP 5.4.0. Using traits, you can have snippets of code "mixed in" to other classes, a concept known as "horizontal reuse".
If you are not looking for traits, it's possible that you could do what you wanted with Runkit, however I would suggest staying as far away from it as possible, if you are not genuinely interested in PHP internals as well.
In any event, whatever you are trying to do is very interesting
I got it to work with dependency injection. The pvar has to be public or create a __get method to return the private variable. I also used the function name because it seems cleaner to me to use it via name rather than it's position in the list but if you want to keep that then just put $key where you see $value from the line: $this->function_list[$value] = ...
function operate($y, $that)
{
return $that->Pvar * $y;
}
class Example {
public $function_list = array();
private $Pvar = 5;
public function __construct()
{
$list = get_defined_functions();
$that = $this;
foreach ($list['user'] as $key => $value) {
$this->function_list[$value] = function() use ($value, $that) {
print call_user_func_array($value, array_merge(func_get_args(), array($that )));
};
}
}
public function __get($key)
{
if (isSet($this->$key)) {
return $this->$key;
} else {
throw new \Exception('Key "'.$key.'" does not exist');
}
}
}
$Ex = new Example();
$Ex->function_list['operate'](10);
If you want to extend MyClass from your modules (and not to initialize it, like in your example code), than you could do it in a way like this:
<?php
namespace modules\MyModuleA;
class MyClassExtension
{
private $MyObject;
public function __construct(\MyClass $MyObject)
{
$this->MyObject = $MyObject;
}
public function doSomething($anyParameter)
{
return $this->MyObject->doSomethingElse($anyParameter * 5, 42, 'foo');
}
}
And MyClass:
<?php
class MyClass extends \Extensible
{
// some code
}
abstract class Extensible
{
private $extensions = [];
public function extend($extension)
{
$this->extensions[] = $extension;
}
public function __call($methodName, $parameters)
{
foreach ($this->extensions as $Extension) {
if (in_array($methodName, get_class_methods($Extension))
return call_user_func_array([$Extension, $methodName], $parameters);
}
throw new \Exception('Call to undefined method ' . $methodName . '...');
}
public function hasExtension($extensionName)
{
return in_array($this->extensions, $extensionName);
}
}
And put it all together:
<?php
$moduleNames = ['MyModuleA', 'MyModuleB'];
$MyObject = new \MyClass;
foreach ($moduleNames as $moduleName) {
$className = '\\modules\\' . $moduleName . '\\MyClassExtension';
$module = new $className($MyObject);
$MyObject->extend($module);
}
// Now you can call a method, that has been added by MyModuleA:
$MyObject->doSomething(10);
You should add an interface for the extension classes of course...
The problem is: What happens if any code in your application calls a method of $MyObject, that is not there, because the module has not been loaded. You would always have to check if ($MyObject->hasExtension('ModuleA')) { ... }, but, of course, the application shouldn't be aware of any module. So I would not design an application in such a way.
I would suggest to use traits (mix-ins). See PHP reference
If you can have another class in that file instead of file with functions
- the best solution will be Traits
http://php.net/manual/en/language.oop5.traits.php
or using inheritance
If you move that code to class you can avoid a lot of unnecessary code. I mean:
include('additional.functions.php');
$arr = get_defined_functions();
$this->new_Functions = $arr['user'];
// trying to call the function with parameter 10
call_user_func(array($this, $this->new_Functions[0]), 10);
It'll be e.g.:
class myClass extends MyBaseClassWithMyAwesomeFunctions
{
private $Pvar = 5;
}
Maybe this approach helps you:
In the files with the additional functions, don't define named functions, but return a closure, that expects (at least) the object (instance of MyClass) as parameter:
<?php
// additional.functions.php
return function ($myObject) {
$Object->multiplyPvar($myObject->getTheNumber());
$Object->doSomethingElse(42, 'foo');
};
The client, that builds MyClass collects those functions from the files into the array:
<?php
$files = [
'/path/to/my/additional.functions1.php',
'/path/to/my/additional.functions2.php'
];
$initFunctions = [];
foreach ($files as $path)
$initFunctions[] = include $path;
$MyObject = new \MyClass($initFunctions);
The constructor then calls those functions:
<?php
class MyClass
{
public function __construct(array $additionalInitFunctions)
{
foreach ($additionalInitFunctions as $additionalInitFunction)
$additionalInitializerFunction($this); // you can also add parameters of course
}
}
This way the class keeps very well testable as well as the function files. Maybe this could help you in any way. You should never ever think about modifying the internal (private) state of an object directly from any code from outside of the class. This is not testable! Think about writing tests before you implement your code (called "test driven development"). You will see, it is not possible to test a class, if you allow any code outside of that class to modify the internal (private) state of the class instance. And you don't want to have this. If you change some internal implementation detail in your class without breaking the unit test of that class, you will anyways probably break some code in any of your additional.functions.php files and no test will tell you: "Hey: you've broken something right now".
I am wondering whether or not it is possible to elegantly map the results of a PDO query to an array member in a class rather than have them floating about as public properties of that object.
Say I have the (condensed) following:
class DBObject {
protected
$record = array();
function __construct(array $record) {
if(!empty($record)) {
$this->loadRecord($record);
}
}
}
Ideally, I want to call the constructor with an array of values passed from the database, rather than use __set or any other weird methods. So using PDO's existing API would be great.
My rough get_all function at the moment has got this far:
static function get_all() {
$class = get_called_class();
$results = DB::factory()->query('SELECT * FROM ' . $class . ' ORDER BY ID');
$results->setFetchMode(PDO::FETCH_CLASS|PDO::FETCH_PROPS_LATE, $class);
return $results;
}
NB: I'm running PHP 5.3 and MySQL through PDO, and already know this problem is solveable using __set, but I explicitly want to avoid using it in favour of something more performant.
You don't need to pass arguments to a constructor to make a class with private members using PDO::FETCH_CLASS. You can do something like this:
<?php
class Songs
{
private $artist;
private $title;
public function __construct()
{
}
public function get_artist()
{
return $this->artist;
}
public function get_title()
{
return $this->title;
}
private function set_artist($artist)
{
$this->artist = $artist;
}
private function set_title($title)
{
$this->title = $title;
}
}
I'm actually doing that on a demo site that I built. It works just fine with PDO::FETCH_CLASS. By default, FETCH_CLASS creates objects by populating the fields BEFORE the constructor. Think of it as bypassing the constructor. And it will do this with private members.
If you'd rather pass arguments to the constructor you can do your query like this:
$obj = $statement->fetchALL(PDO::FETCH_CLASS|PDO::FETCH_PROPS_LATE, 'Songs', $params);
In that case your constructor would look like this:
public function __construct($params)
{
$this->artist = $params[0]['artist'];
$this->title= $params[0]['title'];
}
Removed previous code
Right, can't you do something like this:
class DBObject {
protected $record = array();
function __construct($record = null) {
if(null === $record){
$obj_vars = get_object_vars($this);
$cls_vars = get_class_vars(get_class($this));
$this->$record = array_diff_key($obj_vars, $cls_vars);
}else{
$this->record = $record;
}
}
}
The problem with this however is that the values are still available as public members.
But what it will do is compare 'pre-defined' (class) members to the actual (object) members.
Since PDO will create new members in the object you can use array_diff_key to get the 'new' members.
Yes, this will still not pass them through your constructor.
How about using magic __set() method:
<?php
class MyClass
{
protected $record = array();
function __set($name, $value) {
$this->record[$name] = $value;
}
}
$pdo = new PDO("mysql:host=localhost;dbname=db", 'user', 'password');
$results = $pdo->query('SELECT * FROM table');
$results->setFetchMode(PDO::FETCH_CLASS, 'MyClass');
PHP will call this magic method for every non-existent property passing in its name and value.