I've a shared $sdk object reference (it's Facebook PHP SDK) between many objetcs. I need to save the access token at very beginning of a function call and restore after $this->sdk->api call. See for example getAlbums() function.
How can automatically execute a callback before/after every function call on every FBItem instance?
abstract class Item
{
protected $id, $sdk, $auth;
public function __construct(Facebook $sdk, $auth = null)
{ $this->sdk = $sdk; $this->auth = $auth; }
public function getAlbums() // Require access token change
{
// Am i FBUser or FBPage? Call setAccessToken to set auth
$backup = $this->sdk->getAccessToken();
$this->sdk->setAccessToken($auth ?: $backup);
$as = array();
$rs = $this->sdk->api(sprintf('/%s/albums', $this->id));
foreach($rs['data'] as $i) $as[] = new Album($this->sdk, $this->auth);
// Restore previous token backup
$this->sdk->setAccessToken($backup);
}
}
class User extends Item
{
$ps = array(); $rs = $this->sdk->api(sprintf('/%s/accounts', $this->id));
foreach($rs['data'] as $i) $ps[] = new Page($this->sdk, $i['access_token']);
return $ps;
}
class Page extends Item { }
Probably the only way (without using php rootkit or rewriting whole classes) is preparing wrapper such as this:
class FBItemWrapper {
public $item = new FBItem();
public function __call( $functionName, $args){
// Pre callback
$result = call_user_func_array( array( $this->item, $functionName), $args);
// Post callback
return $result;
}
}
You may set object dynamically so one Wrapper will be enough for everything.
Related
Is there a way to use a object variable instantiated from a class in two functions?
Here's the code I've tried, but its just returning null:
class bookAppointmentsController extends APIController
{
private $business;
public funcition check($key)
{
$this->business = new APIClass();
$setconnection = $this->business->connectAPI($key);
}
public function book()
{
dd($this->business) //returns null
$this->business->book();
}
}
I am trying to use the $business object in two functions but it does not work, when I dd($business) it returns null
Any way to do this?
Move the instantiation to the constructor:
public function __construct(APIClass $business)
{
$this->business = $business;
}
However, it would be better if you make Laravel do the heavy lifting and prepare the APIClass for you.
In your AppServicePorvider under the register method, you can create the APIClass
/**
* Register any application services.
*
* #return void
*/
public function register()
{
$this->app->bind('APIClass', function ($app) {
$api = new APIClass();
// Do any logic required to prepare and check the api
$key = config('API_KEY');
$api->connectAPI($key);
return $api;
});
}
Check the documentations for more details.
Maybe the solution could be to make the variable Global
You could make the variable global:
function method( $args ) {
global $newVar;
$newVar = "Something";
}
function second_method() {
global $newVar;
echo $newVar;
}
Or you could return it from the first method and use it in the second method
public function check($key)
{
$this->business = new APIClass();
$setconnection = $this->business->connectAPI($key);
return $this->business;
}
public function book()
{
$business = check($key);
$business->book();
}
I'm trying to accomplish the following syntax in my code:
$data = new Data();
$user = $data -> user -> get(1);
$product = $data -> product -> get(1);
By using:
class Data {
public $user = null;
public $product = null;
public $a = null;
...
function __construct() {
this -> user = new User();
this -> product = new Product();
this -> a = new A();
...
}
}
The problem with the code is that I will have lots of unused instances inside the data class because I will not use them all in specific scenarios. How can I prevent this?
At a very basic level, you can do something like this, you define a getter for the user property, and the object only gets instantiated when you call it for the first time.
class Data {
protected $user = null;
public function user()
{
if ($this->user === null) {
$this->user = new User();
}
return $this->user;
}
}
You could use aggregation, which means that you pass an object into the class, that way the class is getting either null or the object and you save resources by not initializing everything at once. Here's a decent post about it (not mine).
It's basically this:
class Test {
public $a = '';
public function __construct($object) {
$this->a = $object;
}
}
I would say you could try something like this:
class ThisOne{
protected $user = null;
public function user()
{
if ($this->user === null) {
$this->user = new User();
}
return $this->user;
}
}
The getter only gives you an object the first time it is called!
Implementation, that I use - http://symfony.com/blog/cross-application-links
// apps/backend/config/backendConfiguration.class.php
class backendConfiguration extends sfApplicationConfiguration
{
protected $frontendRouting = null;
public function generateFrontendUrl($name, $parameters = array())
{
return 'http://frontend.example.com'.$this->getFrontendRouting()->generate($name, $parameters);
}
public function getFrontendRouting()
{
if (!$this->frontendRouting)
{
$this->frontendRouting = new sfPatternRouting(new sfEventDispatcher());
$config = new sfRoutingConfigHandler();
$routes = $config->evaluate(array(sfConfig::get('sf_apps_dir').'/frontend/config/routing.yml'));
$this->frontendRouting->setRoutes($routes);
}
return $this->frontendRouting;
}
}
All work fine, but first call generateFrontendUrl function execute approximately 1-2 second.
How to correct this?
I am working with lemonade-php. My code is at https://github.com/sofadesign/limonade.
The issue I am having is when I try to run
class syscore {
public function hello(){
set('post_url', params(0));
include("./templates/{$this->temp}/fullwidth.tpl");
return render('fullwidth');
}
}
which then loads the fullwidth.tpl and runs function fullwidth
fullwidth.tpl
<?php
global $post;
function fullwidth($vars){
extract($vars);
$post = h($post_url);
}
print_r($this->post($post));
?>
it seems to pass the $post_url but I can not pass it again to the print_r($this->post($post));
However when I try to run print_r($this->post($post)) inside the fullwidth function it says it can not find the post() function
I have tried a number of things like below
function fullwidth($vars){
extract($vars);
$post = h($post_url);
print_r(post($post));
}
I tried re connecting to the syscore by
$redi = new syscore();
$redi->connection() <-- this works
$redi->post($post) <-- this does not
Here is my full class syscore
class syscore {
// connect
public function connect($siteDBUserName,$siteDBPass,$siteDBURL,$siteDBPort, $siteDB,$siteTemp){
for ($i=0; $i<1000; $i++) {
$m = new Mongo("mongodb://{$siteDBUserName}:{$siteDBPass}#{$siteDBURL}:{$siteDBPort}", array("persist" => "x", "db"=>$siteDB));
}
// select a database
$this->db = $m->$siteDB;
$this->temp = $siteTemp;
}
public function hello(){
set('post_url', params(0));
include("./templates/{$this->temp}/fullwidth.tpl");
return render('fullwidth');
}
public function menu($data)
{
$this->data = $data;
$collection = $this->db->redi_link;
// find everything in the collection
//print $PASSWORD;
$cursor = $collection->find(array("link_active"=> "1"));
if ($cursor->count() > 0)
{
$fetchmenu = array();
// iterate through the results
while( $cursor->hasNext() ) {
$fetchmenu[] = ($cursor->getNext());
}
return $fetchmenu;
}
else
{
var_dump($this->db->lastError());
}
}
public function post($data)
{
$this->data = $data;
$collection = $this->db->redi_posts;
// find everything in the collection
//print $PASSWORD;
$cursor = $collection->find(array("post_link"=> $data));
if ($cursor->count() > 0)
{
$posts = array();
// iterate through the results
while( $cursor->hasNext() ) {
$posts[] = ($cursor->getNext());
}
return $posts;
}
else
{
var_dump($this->db->lastError());
}
}
}
It looks like you are having some issues understanding the execution path that PHP is taking when trying to render your template. Let's take a more in-depth look, shall we?
// We're going to call "syscore::hello" which will include a template and try to render it
public function hello(){
set('post_url', params(0)); // set locals for template
include("./templates/{$this->temp}/fullwidth.tpl"); // include the template
return render('fullwidth'); // Call fullwidth(array('post_url' => 'http://example.com/path'))
}
The trick to solving this one is to understand how PHP include works. When you call include("./templates/{$this->temp}/fullwidth.tpl") some of your code is executing in the scope of the syscore object, namely:
global $post;
and
print_r($this->post($post));
fullwidth is created in the global scope at this point, but has not yet been called. When render calls fullwidth you're no longer in the syscore scope, which is why you cannot put $this->post($post) inside without triggering an error.
Ok, so how do we solve it? Glad you asked.
We could probably refactor syscore::post to be a static method, but that would then require syscore::db to be static, and always return the SAME mongodb instance (singleton pattern). You definitely do not want to be creating 1000 Mongo instances for each syscore instance.
We could just abuse the framework. A much poorer solution, but it will get the job done.
fullwidth.tpl
<?php
function fullwidth($vars){
$post_url = ''; // put the variables you expect into the symbol table
extract($vars, EXTR_IF_EXISTS); // set EXTR_IF_EXISTS so you control what is added.
$syscore_inst = new syscore;
$post = h($post_url);
print_r($syscore->post($post)); // I think a puppy just died.
}
Look the second way is a complete hack, and writing code like that will probably mean you won't get promoted. But it should work.
But let's say you wanted to get promoted, you would make good, shiny code.
// Note: Capitalized class name
class Syscore {
protected static $_db;
public static function db () {
if (! static::$_db) {
static::$_db = new Mongo(...);
}
return static::$_db;
}
// #FIXME rename to something more useful like "find_posts_with_link"
public static post($url) {
$collection = static::db()->redi_posts;
// find everything in the collection
$cursor = $collection->find(array("post_link"=> $url));
// Changed to a try-catch, since we shouldn't presume an empty find is
// an error.
try {
$posts = array();
// iterate through the results
while( $cursor->hasNext() ) {
$posts[] = ($cursor->getNext());
}
return $posts;
} catch (Exception $e) {
var_dump($this->db->lastError());
}
}
}
Then in your fullwidth function, we don't have to do any of that stupid nonsense of treating an instance method like it were a static method.
function fullwidth($vars){
$post_url = ''; // put the variables you expect into the symbol table
extract($vars, EXTR_IF_EXISTS); // set EXTR_IF_EXISTS so you control what is added.
$post = h($post_url);
print_r(Syscore::post($post)); // static method. \O/ Rainbows and unicorns.
}
In this little example below in PHP what would be a good way to be able to create the variables in the user class and then be able to use them on any page where I create a user object?
<?PHP
//user.class.php file
class User
{
function __construct()
{
global $session;
if($session->get('auto_id') != ''){
//set user vars on every page load
$MY_userid = $session->get('auto_id'); //user id number
$MY_name = $session->get('disp_name');
$MY_pic_url = $session->get('pic_url');
$MY_gender = $session->get('gender');
$MY_user_role = $session->get('user_role');
$MY_lat = $session->get('lat');
$MY_long = $session->get('long');
$MY_firstvisit = $session->get('newregister');
}else{
return false;
}
}
}
?>
<?PHP
// index.php file
require_once $_SERVER['DOCUMENT_ROOT'].'/classes/user.class.php';
$user = new User();
//how should I go about making the variables set in the user class available on any page where I initiate the user class?
// I know I can't use
// echo $MY_pic_url;
// 1 way I can think of is to have the class return an array but is there another way?
?>
To elaborate on Lance' answer; if the point of the class is to be nothing more than an container for the data, in stead of doing something with the data you're pretty safe. But a good principal of OOP is to stick to encapsulation. Encapsulation means, amongst other things, that you hide the inner details of your object from the outside and only let the outside access the fields through it's interface methods.
Let's say you don't want the fields in the User object to be altered from the outside, but only accessed, then you'ld be better of with something like the following:
class User
{
private $_userId;
// and a bunch of other fields
public function __construct( $data )
{
// do something with the data
}
public function getUserId()
{
return $this->_userId;
}
// and a bunch of other getters to access the data
}
In all honesty, you could use magic methods like __set and __get to simulate what you want and catch any unwanted altering in the __set method.
Furthermore, I wouldn't use the session as a global variable. You should pass the session object as an argument to it's constructor (like I illustrated in the example). This enforces loose coupling. Because now your User objects are tied to the global session object, but with passing it to the constructor any data could be passed in. This makes the class more flexible.
Edit:
Here's an example of how you could pass an object (for instance your session object) to the constructor. One thing to keep in mind is that, the way your session object is designed, it still, somewhat, enforces tight coupling, because it mandates getting properties through the get() method.
class User
{
public function __construct( $data )
{
$this->_id = $data->get( 'id' );
$this->_firstname = $data->get( 'firstname' );
// etc
}
}
// usage
$session = new YourSessionObject();
$user = new User( $session );
You have a few options at hand to propagate loose coupling, and making you User object a little more flexible.
Mandate that the data for you User object is provided as:
distinct arguments
class User
{
protected $_id;
protected $_firstname;
// etc;
public function __construct( $id, $firstname, /* etc */ )
{
$this->_id = $id;
$this->_firstname = $firstname;
// etc
}
}
// usage
$session = new YourSessionObject();
$user = new User( $session->get( 'id' ), $session->get( 'firstname' ), /* etc */ );
array
class User
{
protected $_fields = array(
'id' => null,
'firstname' => null,
// etc
);
// dictate (type hint) that the argument should be an array
public function __construct( array $data )
{
foreach( $data as $field => $value )
{
if( array_key_exists( $field, $this->_fields )
{
$this->_fields[ $field ] = $value;
}
}
}
}
// usage
$session = new YourSessionObject();
$array = /* fill this array with your session data */;
$user = new User( $array );
implementing some interface
// objects that implement this interface need to implement the toArray() method
interface Arrayable
{
public function toArray();
}
class User
{
protected $_fields = array(
'id' => null,
'firstname' => null,
// etc
);
// dictate (type hint) that the argument should implement Arrayable interface
public function __construct( Arrayable $data )
{
// get the data by calling the toArray() method of the $data object
$data = $data->toArray();
foreach( $data as $field => $value )
{
if( array_key_exists( $field, $this->_fields )
{
$this->_fields[ $field ] = $value;
}
}
}
}
class YourSessionObject implements Arrayable
{
public function toArray()
{
/* this method should return an array of it's data */
}
}
// usage
$session = new YourSessionObject();
$user = new User( $session );
etc
There are a few other options, but this should give you some ideas. Hope this helps.
Make them public members:
class user
{
public $first_name;
function __construct()
{
$this->first_name = $_SESSION['first_name'];
}
}
$user = new user();
echo $user->first_name;
Sidenote: the constructor has no return value, i.e. return false does not have the effect you probably intended.
Either use public properties or protected properties+accessor methods.
Or store the $session in your object and then "delegate" each query for a property to that $session object.
class User
{
protected $session;
function __construct($session)
{
$this->session = $session;
}
function get($name) {
if( ''==$this->session->get('auto_id')) {
throw new Exception('...');
}
return $this->session->get($name);
}
}
$user = new User($session);
echo $user->get('disp_name');
Or use the "magic" __get() method, e.g.
class User
{
protected static $names = array(
'auto_id', 'disp_name', 'pic_url', 'gender',
'user_role', 'lat', 'long', 'newregister'
);
protected $properties = array();
function __construct()
{
global $session;
if($session->get('auto_id') != '') {
foreach(self::$names as $n) {
$this->properties[$n] = $session->get($n);
}
}
else {
throw new Exception('...');
}
}
function __get($name) {
return isset($this->properties[$name]) ? $this->properties[$name] : null;
}
}
$user = new User;
echo $user->disp_name;
Use attributes to store it.
<?PHP
//user.class.php file
class User
{
public $MY_userid;
public $MY_name;
public $MY_pic_url;
public $MY_gender;
public $MY_user_role;
public $MY_lat;
public $MY_long;
public $MY_firstvisit;
function __construct()
{
global $session;
if($session->get('auto_id') != ''){
//set user vars on every page load
$this->MY_userid = $session->get('auto_id'); //user id number
$this->MY_name = $session->get('disp_name');
$this->MY_pic_url = $session->get('pic_url');
$this->MY_gender = $session->get('gender');
$this->MY_user_role = $session->get('user_role');
$this->MY_lat = $session->get('lat');
$this->MY_long = $session->get('long');
$this->MY_firstvisit = $session->get('newregister');
}else{
return false;
}
}
}
?>
You can also save the user object in the $_SESSION variable after you have created it initially.
<?PHP
//user.class.php file
class User
{
function __construct()
{
var $MY_userid;
var $MY_name;
var $MY_pic_url;
var $MY_gender;
var $MY_user_role;
var $MY_lat;
var $MY_long;
var $MY_firstvisit;
global $session;
if($session->get('auto_id') != ''){
//set user vars on every page load
$this->MY_userid = $session->get('auto_id'); //user id number
$this->MY_name = $session->get('disp_name');
$this->MY_pic_url = $session->get('pic_url');
$this->MY_gender = $session->get('gender');
$this->MY_user_role = $session->get('user_role');
$this->MY_lat = $session->get('lat');
$this->MY_long = $session->get('long');
$this->MY_firstvisit = $session->get('newregister');
}else{
return false;
}
}
}
?>
<?PHP
// index.php file
require_once $_SERVER['DOCUMENT_ROOT'].'/classes/user.class.php';
$user = new User();
print $user->MY_name;
?>