      1: <?php
      2: /**
      3:  *
      4:  */
      5: abstract class Sidecar_Singleton_Base {
      6:
      7:   /**
      8:    * @var Sidecar_Singleton_Base
      9:    */
     10:   private static $_instances = array();
     11:
     12:   /**
     13:    * Bool to holds on to >=PHP 5.3 check
     14:    */
     15:   private static $_is_php53;
     16:
     17:   /**
     18:    * @param array $args
     19:    */
     20:   function __construct( $args = array() ) {
     21:     if ( ! isset( self::$_is_php53 ) )
     22:       self::$_is_php53 = version_compare( PHP_VERSION, '5.3.0', '>=' );
     23:
     24:     $this_class = get_class( $this );
     25:
     26:     if ( isset( self::$_instances[$this_class] ) ) {
     27:       $message = __( '%s is a singleton class and cannot be instantiated more than once.', 'sidecar' );
     28:       Sidecar::show_error( $message , self::_get_called_class() );
     29:       exit;
     30:     }
     31:
     32:     self::$_instances[$this_class] = &$this;
     33:
     34:     if ( method_exists( $this, 'on_load' ) ) {
     35:       $this->on_load( $args );
     36:     }
     37:   }
     38:
     39:   /**
     40:    * @return Sidecar_Singleton_Base
     41:    */
     42:   static function this() {
     43:     return self::$_instances[self::_get_called_class()];
     44:   }
     45:
     46:   /**
     47:    * Clean syntax to access the value on an instance variable for a Singleton class
     48:    *
     49:    * @param $instance_var_name
     50:    *
     51:    * @return mixed
     52:    */
     53:   static function get( $instance_var_name ) {
     54:     $instance = self::_get_instance( self::_get_called_class() );
     55:     return isset( $instance->$instance_var_name ) ? $instance->$instance_var_name : null;
     56:   }
     57:
     58:   /**
     59:    * Clean syntax to call a method on an instance variable for a Singleton class
     60:    *
     61:    * @param string $method_name
     62:    *
     63:    * @return mixed
     64:    */
     65:   static function call( $method_name ) {
     66:     if ( method_exists( $called_class = self::_get_called_class(), $method_name ) ) {
     67:       $args = func_get_args();
     68:       array_shift( $args );
     69:       $result = call_user_func_array( array( self::_get_instance( $called_class ), $method_name ), $args );
     70:     }
     71:     return isset( $result ) ? $result : null;
     72:   }
     73:
     74:   /**
     75:    *
     76:    */
     77:   private static function _get_instance( $called_class ) {
     78:     if ( ! isset( self::$_instances[$called_class] ) )
     79:       self::$_instances[$called_class] = new $called_class();
     80:     return self::$_instances[$called_class];
     81:   }
     82:
     83:   /**
     84:      * Adds a filter hook for an object
     85:      *
     86:      *  $this->add_filter( 'wp_title' );
     87:      *  $this->add_filter( 'wp_title', 11 );
     88:      *  $this->add_filter( 'wp_title', 'special_func' );
     89:      *  $this->add_filter( 'wp_title', 'special_func', 11 );
     90:      *  $this->add_filter( 'wp_title', array( __CLASS__, 'class_func' ) );
     91:      *  $this->add_filter( 'wp_title', array( __CLASS__, 'class_func' ), 11 );
     92:      *
     93:      * @param string $action_name
     94:      * @param bool|int|string|array $callable_or_priority
     95:      * @param int $priority
     96:      * @return mixed
     97:      */
     98:     function add_filter( $action_name, $callable_or_priority = false, $priority = 10 ) {
     99:     $value = false;
    100:         if ( ! $callable_or_priority ) {
    101:             $value = add_filter( $action_name, array( $this, $action_name ), $priority, 99 );
    102:         } else if ( is_numeric( $callable_or_priority ) ) {
    103:             $value = add_filter( $action_name, array( $this, $action_name ), $callable_or_priority, 99 );
    104:         } else if ( is_string( $callable_or_priority ) ) {
    105:             $value = add_filter( $action_name, array( $this, $callable_or_priority ), $priority, 99 );
    106:         } else if ( is_array( $callable_or_priority ) ) {
    107:             $value = add_filter( $action_name, $callable_or_priority, $priority, 99 );
    108:         }
    109:         return $value;
    110:     }
    111:     /**
    112:      * Adds an action hook for an objectx`x``x``  `
    113:      *
    114:      *  $this->add_action( 'init' );
    115:      *  $this->add_action( 'init', 11 );
    116:      *  $this->add_action( 'init', 'special_func' );
    117:      *  $this->add_action( 'init', 'special_func', 11 );
    118:      *  $this->add_action( 'init', array( __CLASS__, 'class_func' ) );
    119:      *  $this->add_action( 'init', array( __CLASS__, 'class_func' ), 11 );
    120:      *
    121:      * @param string $action_name
    122:      * @param bool|int|string|array $callable_or_priority
    123:      * @param int $priority
    124:      * @return mixed
    125:      */
    126:     function add_action( $action_name, $callable_or_priority = false, $priority = 10 ) {
    127:         $this->add_filter( $action_name, $callable_or_priority, $priority );
    128:     }
    129:
    130:   /**
    131:    * Return name of calling class.
    132:    *
    133:    * Provides (hacky) support for PHP < 5.3.
    134:    *
    135:    * @return string
    136:    */
    137:   protected static function _get_called_class() {
    138:     static $classes = array();
    139:     if ( self::$_is_php53 && ( ! defined( 'SIDECAR_DEBUG' ) || ! SIDECAR_DEBUG ) ) {
    140:       return get_called_class();
    141:     } else {
    142:       /**
    143:        * Simulate get_called_class() for < PHP 5.3
    144:        * @see https://stackoverflow.com/a/7904487/102699 for our inspiration
    145:        */
    146:       $backtrace = debug_backtrace( false );
    147:       $class_names = implode( '|', array_keys( self::$_instances ) );
    148:       for( $level = 1; $level < count( $backtrace ); $level++ ) {
    149:         $call = $backtrace[$level];
    150:         $key = "{$call['file']}/{$call['function']}/{$call['line']}";
    151:         if ( isset( $classes[$key] ) ) {
    152:           break;
    153:         } else {
    154:           if ( empty( $call['file'] ) ) {
    155:             /**
    156:              * Added this for 5.2.x
    157:              */
    158:             continue;
    159:           }
    160:           $lines = file($call['file']);
    161:           preg_match_all(
    162:             "#({$class_names})::{$call['function']}(\s*|=|\()#",
    163:             $lines[$call['line']-1],
    164:             $matches
    165:           );
    166:           unset( $lines );
    167:           if ( 0 == count( $matches[1] ) ) {
    168:             continue;
    169:           } if ( 1 < count( $matches[1] ) ) {
    170:             $calls = implode( "::{$call['function']}() or ", $matches[1] ) . "{$call['function']}()";
    171:             trigger_error( sprintf( __( 'Too many calls to static method ::%s() on line %d of %s; can only have one of: %s', 'sidecar' ),
    172:               $call['function'], $call['line'], $call['file'], $calls
    173:             ));
    174:           } else {
    175:             $classes[$key] = $matches[1][0];
    176:             break;
    177:           }
    178:         }
    179:       }
    180:
    181:       if ( ! isset( $classes[$key] ) ) {
    182:         /**
    183:          * Added this for 5.2.x on uninstall
    184:          */
    185:         foreach( $backtrace as $call ) {
    186:           if ( ! empty( $call['function'] ) && 'call_user_func_array' == $call['function'] &&
    187:                ! empty( $call['file'] ) && preg_match( '#/wp-includes/plugin\.php$#', $call['file'] ) &&
    188:                ! empty( $call['args'][0][0] ) && preg_match( "#^({$class_names})$#", $call['args'][0][0] ) ) {
    189:             $key = "{$call['file']}/{$call['function']}/{$call['line']}";
    190:             $classes[$key] = $call['args'][0][0];
    191:             break;
    192:           }
    193:         }
    194:       }
    195:       return $classes[$key];
    196:     }
    197:   }
    198:
    199: //  static function instantiate() {
    200: //    $class_name = self::_get_hooked_class();
    201: //    self::$_instances[$class_name] = new $class_name();
    202: //  }
    203:
    204:   /**
    205:    * @return bool
    206:    */
    207:   private static function _get_hooked_class() {
    208:     $hooked_class = false;
    209:     $backtrace = debug_backtrace( false );
    210:     for( $index = 2; $index < count( $backtrace ); $index++ ) {
    211:       if ( 'call_user_func_array' == $backtrace[$index]['function'] ) {
    212:         $hooked_class = $backtrace[$index]['args'][0][0];
    213:         break;
    214:       }
    215:     }
    216:     return $hooked_class;
    217:   }
    218:
    219: }
    220:
    221: