A valid use for traits - php

In my application I have a Config object which my service layer and views have as a class property. Any time I want a config value I have to do
$configValue = $this->config->setting('setting.name');
What I want to be able to do each time I want a setting is
$configValue = $this->config('setting.name');
To accomplish this I have now put this code within my AbstractService and AbstractView I have
protected function config($setting) {
return $this->config->setting($setting);
}
It is only a tiny little bit of code duplication but I would still prefer to not have it so would it be the right choice to use traits here?

Perhaps you're looking for the magic __invoke function (requires 5.3+, but since you use traits I assume you have this):
<?php
class Config {
private $config = array(
'ip' => '127.0.0.1',
'port' => 12345
);
public function setting($option) {
if (!empty($this->config[$option])) {
return $this->config[$option];
}
return false;
}
public function __invoke($option) {
return $this->setting($option);
}
}
$config = new Config();
var_dump($config('ip')); //127.0.0.1
?>
DEMO

Related

Use instance of child class instead of parent class given a condition

I'm working on breaking up a large, monolithic class into several subclasses, but it's too much to do all at once so I'm looking to split them out one by one over several releases as time permits. It's an authentication class that authorizes some channel, so currently it looks like this:
$auth = new Auth($user, $data);
$output = $auth->authChannel($channelName);
Inside Auth, it basically looks like this:
public function __construct($user, $data)
{
$this->user = $user;
$this->data = $data;
}
public function authChannel($channel)
{
$this->setUserData();
if (isset(self::CHANNEL_AUTH_FUNCTIONS[$channel])) {
$authFunction = self::CHANNEL_AUTH_FUNCTIONS[$channel];
return $this->$authFunction();
} else {
// invalid channel
}
}
So self::CHANNEL_AUTH_FUNCTIONS is basically ['channelA' => 'authChannelA', 'channelB' => 'authChannelB'], etc., and all those functions are in this one class.
Now what I want to do, one at a time, is if $legacyChannel => callLegacyFunction() / else $newChannel => instantiate its own class and call auth().
So I put Auth.php into its own namespace and have the new Channel.php class in that same namespace. And Channel extends Auth.
Currently I have this:
public function authChannel($channel)
{
$this->setUserData();
if (isset(self::CHANNEL_AUTH_LEGACY_FUNCTIONS[$channel])) {
$authFunction = self::CHANNEL_AUTH_LEGACY_FUNCTIONS[$channel];
if ($authFunction) {
return $this->$authFunction();
} else {
$authClassName = __NAMESPACE__ . '\\' . ucwords($channel);
$authClass = new $authClassName($user, $data);
return $authClass->auth();
}
} else {
// invalid channel
}
}
Is there a better way to do this? Currently it seems a bit wasteful since two different objects are created and the setUserData() function for example would need to be called again I believe. I'm also wondering if there's a better way to get the dynamic class name other than through __NAMESPACE__ . / . $className.
You'll have to work quite a bit until that code starts looking better. I'll try to suggest as few changes as possible, to make "migration" as painless as possible, although you are a few steps removed from a clean design.
To start with, you can create an AuthStrategyInterface for your new authentication classes.
interface AuthStrategyInterface
{
public function supports(string $channel): bool;
public function auth($user, $data);
}
Each of your new authentication classes should implement this interface. The method supports($channel) is easy enough to understand: if a authentication class can deal with certain channel, it should return true.
Your Auth class would need a way to get these strategies injected. Usually you would do that in the constructor... but to leave your API unchanged we'll just create a setter method for that.
When executing authChannel(), it will first check on the injected strategies to see if any supports the used $channel, and use that if possible. If not, goes back to check your old implementations.
This way you do not need to touch any of the old code as you add new authentication strategies. As you add new implementations, you are gradually strangling the legacy system. At one point no of the old implementations are used, and you can move on to a new code refactoring phase.
class Auth {
private iterable $strategies = [];
public function __construct($user, $data)
{
$this->user = $user;
$this->data = $data;
}
public function setAuthStrategies(iterable $strategies)
{
$this->strategies = $strategies;
}
public function authChannel($channel)
{
$this->setUserData();
// check if any of the new strategies supports
foreach ($this->strategies as $strategy) {
if ($strategy->supports($channel) {
return $strategy->auth($this->user, $this->data);
}
}
// check "legacy" authentication methods.
if (isset(self::CHANNEL_AUTH_FUNCTIONS[$channel])) {
$authFunction = self::CHANNEL_AUTH_FUNCTIONS[$channel];
return $this->$authFunction($this->user, $this->data);
}
// no valid authentication method
return false;
}
}
To use it, you would do something like this:
$fooAuthStrategy = new FooAuthStrategy();
$barAuthStrategy = new BarAuthStrategy();
$bazAuthStrategy = new BazAuthStrategy();
$auth = new Auth($user, $data);
$auth->setAuthStrategies(
[
$fooAuthStrategy,
$barAuthStrategy,
bazAuthStrategy
]
);
$auth->authChannel($channel);
The specifics would change according to how exactly your application is set-up, but something like this would take you further in a good direction than your current approach.
I don't know if I understood the question correctly, but you couldn't do it like that?
public function authChannel($channel)
{
$this->setUserData();
if (!isset(self::CHANNEL_AUTH_LEGACY_FUNCTIONS[$channel])) {
// Invalid channel
return;
}
return self::CHANNEL_AUTH_LEGACY_FUNCTIONS[$channel]
? $this->$authFunction()
: parent::auth();
}

PHP Classes function inside function?

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();

PHP: Using a variable from an external file in my class

I have a settings.php file that looks something like this:
$user="adam";
$pass="something";
then I have a class called funcs.php, in which I have a require "../settings.php" on top, but I don't know how to get the scope of "$user" into my methods...
I'm sure this is an easy fix, but I've been googling in circles for a while with no luck.
My class looks something like this:
<?php
require_once __DIR__."/../settings.php";
class Funcs
{
public function something()
{
// do stuff
return $user;
}
}
you can use the return construct inside file which can return anything, but if you want to return multiple values, it's best to use an array.
settings.php
return array(
'user' => 'adam',
'pass' => 'something'
);
file.php
$settings = require __DIR__.'settings.php';
echo $settings['user']; // adam
To pass everything returned from the file into a class:
class MyClass {
public function __construct(array $array) {
foreach($array as $key => $value) {
$this->{$key} = $value;
}
}
}
$class = new MyClass(require __DIR__ . '/settings.php');
var_dump($class);
/*
class MyClass#1 (2) {
public $user =>
string(4) "adam"
public $pass =>
string(9) "something"
}
*/
I think the issue is that these are global variables used inside a function or class scope.
If you want to access these from within a function scope you could use the "global" keyword, for example:
public function foo() {
global $user;
echo $user;
}
However – using global (and global variables in general) is usually considered pretty smelly these days.
Some alternatives:
Pass the configuration into the constructor of your class, therefore:
$config = array(
'user' => 'adam',
'pass' => 'something'
);
class MyConsumer {
private $config;
public function __construct($config) {
$this->config = $config;
}
}
$instance = new MyConsumer($config);
This is usually termed "dependency injection", because your config can be easily substituted for something else depending on the context (such as in unit tests).
An alternative is to have a singleton class which encapsulates your config, which you'd access something like (not tested but you get the idea):
// production_config.php
return array(
'user' => 'adam',
'password' => 'something'
);
// config.php
class Config {
protected $config;
protected static $instance = null;
protected function __construct() {
$this->config = require('production_config.php');
}
protected function getKey($key) {
if(isset($this->config[$key])) {
return $this->config[$key];
}
// Possibly throw exception or something else if not found
}
public static function get($key) {
if(self::$instance === null) {
self::$instance = new self();
}
return self::$instance->getKey($key);
}
}
So you can go:
$user = Config::get('user');
This is perhaps easier than passing your config into every class, but it's more difficult to substitute your config for testing purposes.

Custom setters and getters in Laravel

Here's a tricky question.
I am building a framework in Laravel and I want my objects to interact with Rackspace in a transparent way.
From now on I made it possible to upload/delete objects without having in mind Rackspace
$model->file = Input::file('thing'); // and it uploads to Rackspace.
The next step I want to achieve is to get the route using my config file. The behaviour would be something like $route = $file->source (with source with hello.jpg in the database for instance) and get $route as rackspace.com/WHATEVER/hello.jpg. The part rackspace.com/WHATEVER is in my config file, so the only thing I need is how to make this behaviour.
I have been searching extensively and I only found the __call() method to do so.
The fields I want to behave like this are dynamic and are setted from an array such as:
public static $rackspaceable = array('source' => 'images-demo');
Where images-demo is a Rackspace container.
Does anyone knows to achieve that and if it is even possible?
This might be what you are looking for:
class Model extends Eloquent {
public static $rackspaceable = array('source' => 'images-demo');
public function __get($key)
{
if (isset(static::$rackspaceable[$key]))
{
return static::$rackspaceable[$key];
}
return parent::__get($key);
}
public function __set($key, $value)
{
if (isset(static::$rackspaceable[$key]))
{
static::$rackspaceable[$key] = $value;
}
else
{
parent::__set($key, $value);
}
}
}
To use it:
$model = new Model;
var_dump( $model->source );
$model->source = 'new value';
var_dump( $model->source );

Global variable inside multiple classes

Let's say I have something like this:
<?php
namespace Twitter;
class Twitter {
function __construct()
{
$config = array ('api' => 'something', 'api_url' => 'something2');
}
// some code goes here
}
class TwitterConnection {
function __construct()
{
$config = array ('api' => 'something', 'api_url' => 'something2');
}
// some code goes here
}
?>
and so on - there will be more classes that uses $config variables.
Now, the question is: how can I define config only once, and make it accessible across all classes?
Thanks
You could create a configuration object that reads from your data source (ini, db, php file ...) and populates itself. Then give it a getter so you can get the configuration properties stored within.
Something along the lines of Config::get('someProperty')
Once you have this object setup, you can pass it to the constructor of your classes so it can be used in inside.
class Twitter {
function __construct($config) {
$state = $config->get('someState');
}
}
You could also simply use it within your classes without injecting it by making it a static class (You could also just as easily create a new instance).
class Twitter {
function __construct() {
//Don't recommend this, better to inject it.
$state = Config::get('someState');
}
}
EDIT
The simplest config class that uses your hardcoded array would look something like this. Again, I suggest you move out your configuration out of your code.
class Config {
private $opts = array();
public function __construct() {
/**
* Ideally opts should be coming from some kind of easy to
* access configuration file
*
*/
$this->opts = array ('api' => 'something', 'api_url' => 'something2');
}
public function get($key) {
if (isset($this->opts[$key])) {
return $this->opts[$key];
}
return false;
}
}
class Twitter {
function __construct($config) {
echo $config->get('api');
}
}
$config = new Config();
new Twitter($config);
You could also change the class a bit so that it works without needing an instance of itself.
There are a couple of different things in play here - config data storage, and config data representation and use.
For storage, as martswite commented above, you could have a config file. You could also have config data stored in a database.
For data representation, you could have an array, like you've shown in your question, or a separate full-fledged object.
Usually, if you have objects that have a dependency on certain data in order to work, you pass (inject) that dependency into the object through its constructor. So, roughly, something like this:
class Example
{
private $dependency;
public function __construct($dependency)
{
$this->dependency = $dependency;
}
public function doSomething()
{
// do something with $this->dependency
}
}
This would be tedious to do manually if you had many objects which required the same dependency. Thankfully, there are dependency injection containers which automate a lot of the process. Where do you find these containers? A Google search should yield results. That said, Symfony seems like a popular option: http://components.symfony-project.org/dependency-injection/
All that said, there's a bit of a learning curve to understanding and using a DI container. Still, it's probably the best way to go without introducing globals into your code.
Finally, just to give you an idea of how to use it, here's some pseudo-code:
// Load config data from file/db/some other source. Use it to populate an object (most likely) or array
// Set up the DI container to automatically inject the config data into the objects which require it
// Profit
Try:
namespace Twitter;
$config = array ('api' => 'something', 'api_url' => 'something2');
class Twitter {
function __construct()
{
global $config;
}
}
class TwitterConnection {
function __construct()
{
global $config;
}
}
?>

Categories