What is the best way to call Method heDidIt() from child class Make?
I was thinking about events but couldnt find a good non global solution.
$control = new Control();
$maker = $control->createMaker();
$maker->doIt();
class Control
{
private $_make;
public function createMaker()
{
$this->_make = new Make();
return $this->_make;
}
private function heDidIt()
{
//Call me if the Maker did something.
}
}
class Make
{
public function doIt()
{
//hey im doing something, better tell my Controller
}
}
Just tell Make who's its boss so it can inform him:
$control = new Control();
$maker = $control->createMaker();
$maker->doIt();
class Control
{
private $_make;
public function createMaker()
{
$this->_make = new Make($this);
return $this->_make;
}
private function heDidIt()
{
//Call me if the Maker did something.
}
public function inform($sampleParam) {
var_dump($sampleParam);
$this->heDidIt();
}
}
class Make
{
protected $control;
public function __construct(Control $control) {
$this->control = $control;
}
public function doIt()
{
//hey im doing something, better tell my Controller
$control->inform('called in Make::doIt()');
}
}
$control = new Control();
$maker = $control->createMaker();
$maker->doIt();
class Control
{
private $_make;
public function createMaker()
{
$this->_make = new Make();
return $this->_make;
}
**protected** function heDidIt()
{
//Call me if the Maker did something.
}
}
class Make **extends Control**
{
public function doIt()
{
**$this -> heDidIt();**
//hey im doing something, better tell my Controller
}
}
Although this seems extremely pointless, so maybe providing your actual code and requirements would let us help you better.
Related
I'm wondering if its possible to switch the visibility in PHP. Let me demonstrate:
class One {
function __construct($id){
if(is_numeric($id)){
//Test function becomes public instead of private.
}
}
private function test(){
//This is a private function but if $id is numeric this is a public function
}
}
Is such thing even possible?
I would use an abstract class with two implementing classes: One for numeric and one for non-numeric:
abstract class One {
static function generate($id) {
return is_numeric($id) ? new OneNumeric($id) : new OneNonNumeric($id);
}
private function __construct($id) {
$this->id = $id;
}
}
class OneNumeric extends One {
private function test() {
}
}
class OneNonNumeric extends One {
public function test() {
}
}
$numeric = One::generate(5);
$non_numeric = One::generate('not a number');
$non_numeric->test(); //works
$numeric->test(); //fatal error
It can be faked up to a point with magic methods:
<?php
class One {
private $test_is_public = false;
function __construct($id){
if(is_numeric($id)){
$this->test_is_public = true;
}
}
private function test(){
echo "test() was called\n";
}
public function __call($name, $arguments){
if( $name=='test' && $this->test_is_public ){
return $this->test();
}else{
throw new LogicException("Method $name() does not exist or is not public\n");
}
}
}
echo "Test should be public:\n";
$numeric = new One('123e20');
$numeric->test();
echo "Test should be private:\n";
$non_numeric = new One('foo');
$non_numeric->test();
I haven't thought about the side effects. Probably, it's only useful as mere proof of concept.
I have the following PHP code as chain of resposibility, I am using PHP5.4.9.
abstract class Logger
{
protected $next;
public function next($next)
{
$this->next = $next;
return $this->next;
}
public function run(){
$this->invoke();
if(null!=$this->next){
$this->next->invoke();
}
}
abstract public function invoke();
}
class EmailLogger extends Logger
{
public function invoke()
{
print_r("email\n");
}
}
class DatabaseLogger extends Logger
{
public function invoke()
{
print_r("database\n");
}
}
class FileLogger extends Logger
{
public function invoke()
{
print_r("file \n");
}
}
$logger = new EmailLogger();
$logger->next(new DatabaseLogger())->next(new FileLogger());
$logger->run();
the expect output is:
email
database
file
but the actually output:
email
database
I hope to implement chain of resposibility design pattern by PHP language, one abstract class and three or more classes to do something as a chain. but only the first two object works.
Anyting missing? Or PHP can not use this coding style under PHP5.4.9?
Thanks.
Replace
public function run() {
$this->invoke ();
if (null != $this->next) {
$this->next->invoke();
}
}
With
public function run() {
$this->invoke ();
if (null != $this->next) {
$this->next->run ();
}
}
please try $this->next->invoke() change $this->next->run()
I have problem in using ExceptionMatcher...My example spec:
class DescribeBall extends \PHPSpec\Context {
private $_ball = null;
function before() {
$this->_ball = $this->spec(new Ball);
}
function itShouldHaveStatusRolledOnRoll() {
$this->_ball->roll();
$this->_ball->getStatus()->should->be('Rolled');
}
function itShouldThrowException() {
$this->_ball->getException()->should->throwException('Exception','Error');
}
}
My example class
class Ball {
private $status = null;
public function roll() {
$this->status = 'Rolled';
}
public function getStatus() {
return $this->status;
}
public function getException() {
throw new Exception('Error');
}
}
Anyone used this matcher with success?
$this->_ball->getException()->should->throwException('Exception','Error');
Thanks to my colleagues:
"The last time I looked at it, it used closures (unless Marcello changed it meanwhile) it should still work like this":
function itShouldThrowException() {
$ball = $this->_ball;
$this->spec(function() use ($ball) {
$ball->getException();
})->should->throwException('Exception','Error');
}
I need to implement the following pattern in php:
class EventSubscriber
{
private $userCode;
public function __construct(&$userCode) { $this->userCode = &$userCode; }
public function Subscribe($eventHandler) { $userCode[] = $eventHandler; }
}
class Event
{
private $subscriber;
private $userCode = array();
public function __construct()
{
$this->subscriber = new Subscriber($this->userCode)
}
public function Subscriber() { return $this->subscriber; }
public function Fire()
{
foreach ($this->userCode as $eventHandler)
{
/* Here i need to execute $eventHandler */
}
}
}
class Button
{
private $eventClick;
public function __construct() { $this->eventClick = new Event(); }
public function EventClick() { return $this->eventClick->Subscriber(); }
public function Render()
{
if (/* Button was clicked */) $this->eventClick->Fire();
return '<input type="button" />';
}
}
class Page
{
private $button;
// THIS IS PRIVATE CLASS MEMBER !!!
private function ButtonClickedHandler($sender, $eventArgs)
{
echo "button was clicked";
}
public function __construct()
{
$this->button = new Button();
$this->button->EventClick()->Subscribe(array($this, 'ButtonClickedHandler'));
}
...
}
what is the correct way to do so.
P.S.
I was using call_user_func for that purpose and believe it or not it was able to call private class members, but after few weeks of development i've found that it stopped working. Was it a bug in my code or was it some something else that made me think that 'call_user_func' is able call private class functions, I don't know, but now I'm looking for a simple, fast and elegant method of safely calling one's private class member from other class. I'm looking to closures right now, but have problems with '$this' inside closure...
Callbacks in PHP aren't like callbacks in most other languages. Typical languages represent callbacks as pointers, whereas PHP represents them as strings. There's no "magic" between the string or array() syntax and the call. call_user_func(array($obj, 'str')) is syntactically the same as $obj->str(). If str is private, the call will fail.
You should simply make your event handler public. This has valid semantic meaning, i.e., "intended to be called from outside my class."
This implementation choice has other interesting side effects, for example:
class Food {
static function getCallback() {
return 'self::func';
}
static function func() {}
static function go() {
call_user_func(self::getCallback()); // Calls the intended function
}
}
class Barf {
static function go() {
call_user_func(Food::getCallback()); // 'self' is interpreted as 'Barf', so:
} // Error -- no function 'func' in 'Barf'
}
Anyway, if someone's interested, I've found the only possible solution via ReflectionMethod. Using this method with Php 5.3.2 gives performance penalty and is 2.3 times slower than calling class member directly, and only 1.3 times slower than call_user_func method. So in my case it is absolutely acceptable. Here's the code if someone interested:
class EventArgs {
}
class EventEraser {
private $eventIndex;
private $eventErased;
private $eventHandlers;
public function __construct($eventIndex, array &$eventHandlers) {
$this->eventIndex = $eventIndex;
$this->eventHandlers = &$eventHandlers;
}
public function RemoveEventHandler() {
if (!$this->eventErased) {
unset($this->eventHandlers[$this->eventIndex]);
$this->eventErased = true;
}
}
}
class EventSubscriber {
private $eventIndex;
private $eventHandlers;
public function __construct(array &$eventHandlers) {
$this->eventIndex = 0;
$this->eventHandlers = &$eventHandlers;
}
public function AddEventHandler(EventHandler $eventHandler) {
$this->eventHandlers[$this->eventIndex++] = $eventHandler;
}
public function AddRemovableEventHandler(EventHandler $eventHandler) {
$this->eventHandlers[$this->eventIndex] = $eventHandler;
$result = new EventEraser($this->eventIndex++, $this->eventHandlers);
return $result;
}
}
class EventHandler {
private $owner;
private $method;
public function __construct($owner, $methodName) {
$this->owner = $owner;
$this->method = new \ReflectionMethod($owner, $methodName);
$this->method->setAccessible(true);
}
public function Invoke($sender, $eventArgs) {
$this->method->invoke($this->owner, $sender, $eventArgs);
}
}
class Event {
private $unlocked = true;
private $eventReceiver;
private $eventHandlers;
private $recursionAllowed = true;
public function __construct() {
$this->eventHandlers = array();
}
public function GetUnlocked() {
return $this->unlocked;
}
public function SetUnlocked($value) {
$this->unlocked = $value;
}
public function FireEventHandlers($sender, $eventArgs) {
if ($this->unlocked) {
//защита от рекурсии
if ($this->recursionAllowed) {
$this->recursionAllowed = false;
foreach ($this->eventHandlers as $eventHandler) {
$eventHandler->Invoke($sender, $eventArgs);
}
$this->recursionAllowed = true;
}
}
}
public function Subscriber() {
if ($this->eventReceiver == null) {
$this->eventReceiver = new EventSubscriber($this->eventHandlers);
}
return $this->eventReceiver;
}
}
As time passes, there are new ways of achieving this.
Currently PSR-14 is drafted to handle this use case.
So you might find any of these interesting:
https://packagist.org/?query=psr-14
Please look at the following code snipped
class A
{
function __get($name)
{
if ($name == 'service') {
return new Proxy($this);
}
}
function render()
{
echo 'Rendering A class : ' . $this->service->get('title');
}
protected function resourceFile()
{
return 'A.res';
}
}
class B extends A
{
protected function resourceFile()
{
return 'B.res';
}
function render()
{
parent::render();
echo 'Rendering B class : ' . $this->service->get('title');
}
}
class Proxy
{
private $mSite = null;
public function __construct($site)
{
$this->mSite = $site;
}
public function get($key)
{
// problem here
}
}
// in the main script
$obj = new B();
$obj->render();
Question is: in method 'get' of class 'Proxy', how I extract the corresponding resource file name (resourceFile returns the name) by using only $mSite (object pointer)?
What about:
public function get($key)
{
$file = $this->mSite->resourceFile();
}
But this requires A::resourceFile() to be public otherwise you cannot access the method from outside the object scope - that's what access modifiers have been designed for.
EDIT:
OK - now I think I do understand, what you want to achieve. The following example should demonstrate the desired behavior:
class A
{
private function _method()
{
return 'A';
}
public function render()
{
echo $this->_method();
}
}
class B extends A
{
private function _method()
{
return 'B';
}
public function render()
{
parent::render();
echo $this->_method();
}
}
$b = new B();
$b->render(); // outputs AB
But if you ask me - I think you should think about your design as the solution seems somewhat hacky and hard to understand for someone looking at the code.