I have part of abstract base class that looks like this:
abstract class Fragment_Cache {
static $in_callback = false;
abstract public function callback( $name, $args );
}
I need to flip $in_callback to true during callback() method execution. That is used in other part of code to prevent nesting of caches (so I want to cache widget and menu, but not menu inside widget).
However since it is abstract method I cannot rely on subclass implementations for that.
I also cannot flip flag on/off around method call, because it is passed on and executed by different library that takes care of running it async instead of during page load.
How can I architecture myself out of this corner? :)
Actual monstrosity that sets up its call:
$output = tlc_transient( 'fragment-cache-' . $this->type . '-' . $name . $salt )
->updates_with( array( $this, 'callback' ), array( $name, $args ) )
->expires_in( $this->timeout )->get();
How about hiding the actual functionality in another function?
abstract class Fragment_Cache {
static $in_callback = false;
public function callback($name, $args)
{
self::$in_callback = true;
$this->doCallback($name, $args);
self::$in_callback = false;
}
abstract protected function doCallback( $name, $args );
}
You need to overwrite doCallback, which will do the actual heavy-lifting, but is not accessible directly from the outside, only via callback.
abstract class Fragment_Cache {
static $in_callback = false;
public function callback( $name, $args ){
self::$in_callback = true;
$ret = $this->__doCallback($name, $args);
self::$in_callback = false;
return $ret;
}
abstract protected function __doCallback($name, $args);
}
Related
Looking for a flexible way to allow other developers to extend render methods for a templating system, basically allowing them to generate their own render::whatever([ 'params' ]) methods.
Current set-up work well from a single developer point of view, I have a number of classes set-up based on context ( post, media, taxonomy etc ), with a __callStatic method collecting the calling function which checks if the method_exists within the class, and if so, extracts any passed arguments and renders the output.
quick example ( pseudo-code ):
-- view/page.php
render::title('<div>{{ title }}</div>');
-- app/render.php
class render {
public static function __callStatic( $function, $args ) {
// check if method exists
if ( method_exists( __CLASS__, $function ){
self::{ $function }( $args );
}
}
public static function title( $args ) {
// do something with the passed args...
}
}
I want to allow developers to extend the available methods from their own included class - so they could create for example render::date( $args ); and pass this to their logic to gather data, before rendering the results to the template.
The questions is, what approach would work best and be performant - errors are safety are not a big concern at this point, that can come later.
EDIT --
I am already making this work by doing the following ( pseudo-code again.. ):
-- app/render.php
class render {
public static function __callStatic( $function, $args ) {
// check if method exists
if (
method_exists( __CLASS__, $function
){
self::{ $function }( $args );
}
// check if method exists in extended class
if (
method_exists( __CLASS__.'_extend', $function
){
__CLASS__.'_extend'::{ $function }( $args );
}
}
public static function title( $args ) {
// do something with the passed args...
}
}
-- child_app/render_extend.php
class render_extend {
public static function date( $args = null ) {
// do some dating..
}
}
The issue here is that this is limited to one extension of the base render() class.
A common way (used by Twig and Smarty, for a couple of examples), is to require developers to manually register their extensions as callables. The render class keeps a record of them, and then as well as checking its own internal methods, also checks this list from _callStatic.
Based on what you have already, this might look like this:
class render
{
/** #var array */
private static $extensions;
public static function __callStatic($function, $args)
{
// check if method exists in class methods...
if ( method_exists( __CLASS__, $function )) {
self::{$function}(self::$args);
}
// and also in registry
elseif (isset(self::$extensions[$function])) {
(self::$extensions[$function])($args);
}
}
public static function title($args)
{
// do something with the passed args...
}
public static function register(string $name, callable $callback)
{
self::$extensions[$name] = $callback;
}
}
A developer would make use of this like so:
render::register('date', function($args) {
// Do something to do with dates
});
Full demo here: https://3v4l.org/oOiN6
I have the following code, perform a global function within a class to fill the functions of wordpress, the problem is that the only way that I could get a variable public class is as follows
class Core {
public $notice;
function __construct(){
$this->core_function();
}
function core_function(){
global $globalvar;
$globalvar = $this;
function notice_global(){
global $globalvar;
return $globalvar->notice;
}
}
function set_notice(){
$this->notice = array('Warning');
}
}
$GP = new Core();
$GP->set_notice();
var_dump(notice_global());
Any other ideas or suggestions, this code is correct or not?
As you said in the comments, you need global function due to wordpress hook method (for a plugin, I suppose).
This is not necessary: there is a way to pass an object method (not a whole object) to wordpress.
You can try in this way:
class Core {
public $notice;
function get_notice()
{ return $this->notice; }
function set_notice()
{ $this->notice = array('Warning'); }
}
$GP = new Core();
$GP->set_notice();
add_action( 'save_post', array( $GP, 'get_notice' ) );
Or - for a better flexibility - in this way:
class Core {
public $notice;
function get_notice()
{ return $this->notice; }
function set_notice()
{ $this->notice = array('Warning'); }
function add_wp_action( $hook, $method )
{ add_action( $hook, array( $this, $method ) ); }
}
$GP = new Core();
$GP->set_notice();
$GP->add_wp_action( 'save_post', 'get_notice' );
By this way, you can directly set all your wp hooks in the class and call they directly with an object method, without using globals variables or function tricks.
I'm not sure if I'm understanding you right, but notice_global can be moved out of that class.
Globals have scope outside of classes
There is no need for these functions. You've defined $notice as public property. You can access it like this: $GP->notice;
You might also want to read documentation on visibility of methods and properties.
Say we have a class with several protected and/or public methods.
I need to perform a check each time a method is called. I could do that check each time i call a method :
class Object
{
// Methods
}
$o = new Object();
if($mayAccess) $o->someMethod();
or
if($mayAccess) $this->someMethod();
But i would like developers neither to have to think about it nor to write it. I've thought about using __call to do :
class Object
{
public function __call($methodName, $args)
{
if($mayAccess) call_user_func_array($this->$methodName, $args);
}
}
Unfortunatly, if i call the method from inside the class, __call will not invoked as it only works when a non-visible method is called.
Is there a clean way to hide this check for both internal and external calls ? Again the goal is to make sure a developper won't forget to do it when calling a method.
Thanks in advance :)
EDIT :
I have another way of doing this :
class Object
{
public function __call($methodName, $args)
{
if($mayAccess) call_user_func_array($methodName, $args);
}
}
function someMethod() { }
But i won't be able to use $this anymore, which means no protected methods, which i do need.
No, I dont think so. What you could do though is write a proxy:
class MayAccessProxy {
private $_obj;
public function __construct($obj) {
$this->_obj = $obj;
}
public function __call($methodName, $args) {
if($mayAccess) call_user_func_array(array($this->_obj, $methodName), $args);
}
}
This means you have to instantiate a proxy for every object you want to check:
$obj = new MayAccessProxy(new Object());
$obj->someMethod();
Ofcourse you'd also want the proxy to behave exactly like the object itself. So you also have to define the other magic methods.
To make it a bit easier for the developers you could do something like this:
class Object {
/**
* Not directly instanciable.
*/
private __construct() {}
/**
* #return self
*/
public static function createInstance() {
$obj = new MayAccessProxy(new self());
return $obj;
}
}
$obj = Object::createInstance();
So what if you made all your methods protected or private? (I know this is old and "answered" question)
The __call magic method intercepts all non-existing and non-public methods so having all your methods not public will allow you to intercepts all of them.
public function __call( $func, $args )
{
if ( !method_exists( $this, $func ) ) throw new Error("This method does not exist in this class.");
Handle::eachMethodAction(); // action which will be fired each time a method will be called
return $this->$func( ...$args );
}
Thanks to that you will not need to do anything to your code (expect adding __call and doing quick replace all) and if your classes have common parent then you can just add it to parent and not care anymore.
BUT
This solution creates two major problems:
The protected/private methods automatically will be available to public
The errors will be pointing to __call not the proper file
What can we do?
Custom private/protected
You can add a list of all protected/private methods and check before the call if the method can be return to public:
public function __call( $func, $args )
{
$private = [
"PrivateMethod" => null
];
if ( !method_exists( $this, $func ) ) throw new Error("This method does not exist in this class.");
if ( isset( $private[$func] ) ) throw new Error("This method is private and cannot be called");
Handle::eachMethodAction(); // action which will be fired each time a method will be called
return $this->$func( ...$args );
}
For many this might be deal breaker, but I personally use this approach only in classes with only public methods (which I set to protected). So if you can, you might separate methods into publicClass and privateClass and eliminate this problem.
Custom Errors and Stack
For better errors I have created this method:
/**
* Get parent function/method details
*
* #param int counter [OPT] The counter allows to move further back or forth in search of methods detalis
*
* #return array trace It contains those elements :
* - function - name of the function
* - file - in which file exception happend
* - line - on which line
* - class - in which class
* - type - how it was called
* - args - arguments passed to function/method
*/
protected function getParentMethod( int $counter = 0 ) {
$excep = new \Exception();
$trace = $excep->getTrace();
$offset = 1;
if ( sizeof( $trace ) < 2 ) $offset = sizeof( $trace ) - 1;
return $trace[$offset - $counter];
}
Which will return details about the previous method/function which called protected method.
public function __call( $func, $args )
{
$private = [
"PrivateMethod" => null
];
if ( !method_exists( $this, $func ) ) {
$details = (object) $this->getParentMethod();
throw new Error("Method $func does not exist on line " . $details->line . ", file: " . $details->file . " invoked by " . get_class($this) . $details->type . $func . " () ");
}
if ( isset($private[$func]) ) {
$details = (object) $this->getParentMethod();
throw new Error("Method $func is private and cannot be called on line " . $details->line . ", file: " . $details->file . " invoked by " . get_class($this) . $details->type . $func . " () ");
}
return $this->$func( ...$args );
}
This is not much of a problem but might lead to some confusion while debugging.
Conclusion
This solution allows you to have control over any call of private/protected methods FROM OUTSIDE OF CLASS. Any this->Method will omit __call and will normally call private/protected method.
class Test {
public function __call( $func, $args )
{
echo "__call! ";
if ( !method_exists( $this, $func ) ) throw new Error("This method does not exist in this class.");
return $this->$func( ...$args );
}
protected function Public()
{
return "Public";
}
protected function CallPublic()
{
return "Call->" . $this->Public();
}
}
$_Test = new Test();
echo $_Test->CallPublic(); // result: __call! Call->Public - it uses two methods but __call is fired only once
If you want to add a similar thing to static methods use __callStatic magic method.
If I have a fairly complex User model that I would like to use the Data Mapping pattern to load, how would I lazily load some of the more intensive bits of user info without allowing the User to be aware of the UserMapper?
For example - if the User model allows for an array of Address objects (and the User might have many of them, but not necessarily needed up front), how would I load those object if/when needed?
Do I make the User model aware of the AddressMapper?
Do I pass the User model BACK into the UserMapper which then hydrates only the Addresses?
Is there a better option?
Well, I have found the following clever pattern at one time, courtesy of Ben Scholzen, developer for the Zend Framework. It goes something like this:
class ModelRelation
implements IteratorAggregate
{
protected $_iterator;
protected $_mapper;
protected $_method;
protected $_arguments;
public function __construct( MapperAbstract $mapper, $method, array $arguments = array() )
{
$this->_mapper = $mapper;
$this->_method = $method;
$this->_arguments = $arguments;
}
public function getIterator()
{
if( $this->_iterator === null )
{
$this->_iterator = call_user_func_array( array( $this->_mapper, $this->_method ), $this->_arguments );
}
return $this->_iterator;
}
public function __call( $name, array $arguments )
{
return call_user_func_array( array( $this->getIterator(), $name ), $arguments );
}
}
Ben Scholzen's actual implementation is here.
The way you would use it, is something like this:
class UserMapper
extends MapperAbstract
{
protected $_addressMapper;
public function __construct( AddressMapper $addressMapper )
{
$this->_addressMapper = $addressMapper;
}
public function getUserById( $id )
{
$userData = $this->getUserDataSomehow();
$user = new User( $userData );
$user->addresses = new ModelRelation(
$this->_addressesMapper,
'getAddressesByUserId',
array( $id )
);
return $user;
}
}
class AddressMapper
extends MapperAbstract
{
public function getAddressesByUserId( $id )
{
$addressData = $this->getAddressDataSomehow();
$addresses = new SomeAddressIterator( $addressData );
return $addresses;
}
}
$user = $userMapper->getUserById( 3 );
foreach( $user->addresses as $address ) // calls getIterator() of ModelRelation
{
// whatever
}
The thing is though; this could get very slow, if the object graphs get very complex and deeply nested at some point, because the mappers all have to query their own data (presuming you are using a database for persistence). I experienced this when I used this pattern for a CMS to get nested Pages objects (arbitrarily deep child Pages).
It could probably be tweaked with some caching mechanism, to speed things up considerably though.
I'm working with a CMS, Joomla, and there's a core class which renders a set of parameters to a form, JParameter. Basically it has a render() function which outputs some table-laden HTML which is not consistent with the rest of my site.
For issues of maintainability, and because I have no idea where else this is being used, I don't want to change the core code. What would be ideal would to be able to define a new class which extends JParameter and then cast my $params object down to this new sub class.
// existing code --------------------
class JParameter {
function render() {
// return HTML with tables
}
// of course, there's a lot more functions here
}
// my magical class -----------------
class MyParameter extends JParameter {
function render() {
// return HTML which doesn't suck
}
}
// my code --------------------------
$this->params->render(); // returns tables
$this->params = (MyParameter) $this->params; // miracle occurs here?
$this->params->render(); // returns nice html
There's always PECL's Classkit but I get a feeling that you'd really rather not do this. Assuming you're directly calling $this->params->render(), you might just want to make a function/object that does an alternate rendering ( MyParamRenderer::render($this->params)) and avoid performing OO gymnastics not natively supported by the language.
What about creating a decorator of sorts that delegates anything apart from JParameter::render() to the existing object
class MyJParameter {
private $jparm;
function __construct( JParameter $jparm ) {
$this->jparm = $jparm;
}
function render() {
/* your code here */
}
function __get( $var ) {
if( isset( $this->$jparm->$var ) {
return $this->$jparm->$var;
}
return false;
}
function __set( $var, $val ) {
/* similar to __get */
}
function __call( $method, $arguments ) {
if( method_exists( $this->jparm, $method ) {
return call_user_func_array( array( $this->jparm, $method ), $arguments );
}
return false;
}
}
Or is this just too smelly?