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.
}
Related
I'm wanting to create a new instance of my Class and assign it's attributes the values that are returned. The reason for this is I'm creating a series of methods inheriting from the calling class, as opposed to using static methods which I already had working.
Example of what I'm using currently:
public static function findById($id) {
$id = self::escapeParam($id);
$idVal = is_int($id) ? "i" : "s";
$sql = "SELECT * FROM ".static::$db_table." WHERE id = ? LIMIT 1";
return static::findByQuery($sql,$idVal,$id);
}
public static function findByQuery($sql,$bindChar = '',$bindVal = '') {
try {
$callingClass = get_called_class();
$object = new $callingClass;
$statement = Database::$connection->prepare($sql);
if(!empty($bindChar)) :
$statement->bind_param($bindChar, $bindVal);
endif;
if($statement->execute()) :
$result = $statement->get_result();
$object = $result->fetch_object();
endif;
$statement->close();
if(!empty($object)) :
return $object;
endif;
} catch(Exception $e) {
}
}
What I tried was writing an instantiation method that creates a new instance of my class, and then assign each attribute of the object the value it returns from an array from a tutorial I did. However, the tutorial was fairly outdated and didn't use any new syntax or binding, so I was trying to rework this.
Example from the tutorial below:
public static function find_by_id($id) {
global $database;
$the_result_array = static::find_by_query("SELECT * FROM " . static::$db_table . " WHERE id = $id LIMIT 1");
return !empty($the_result_array) ? array_shift($the_result_array) : false;
}
public static function find_by_query($sql) {
global $database;
$result_set = $database->query($sql);
$the_object_array = array();
while($row = mysqli_fetch_array($result_set)) {
$the_object_array[] = static::instantation($row);
}
return $the_object_array;
}
public static function instantation($the_record){
$calling_class = get_called_class();
$the_object = new $calling_class;
foreach ($the_record as $the_attribute => $value) {
if($the_object->has_the_attribute($the_attribute)) {
$the_object->$the_attribute = $value;
}
}
return $the_object;
}
private function has_the_attribute($the_attribute) {
return property_exists($this, $the_attribute);
}
What I was trying to do from the tutorial, was to return my result as an array using a while, and then assigning a variable by passing the built array into the static::instantation() method, but it doesn't seem to ever be working correctly, as any public functions I create in my calling class (Admin for example) aren't called after as they don't exist due to the Class not being instantiated.
mysqli_result::fetch_object() accepts the class name as the first argument. You can pass the class name as an argument to that method and get the instance of the model. I am not sure why you have that much code but consider my example which I wrote based on your own code:
<?php
class Model
{
public static function findByQuery(string $sql, ?string $bindChar = null, ?string $bindVal = null): ?static
{
$statement = Database::$connection->prepare($sql);
if ($bindChar) :
$statement->bind_param($bindChar, $bindVal);
endif;
$statement->execute();
$result = $statement->get_result();
return $result->fetch_object(static::class);
}
}
class User extends Model
{
private $id;
}
class Database
{
public static mysqli $connection;
}
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
Database::$connection = new mysqli('localhost', 'user', 'password', 'test');
$user = User::findByQuery('SELECT ? as id', 's', 'Dharman');
var_dump($user);
The output from that example is:
object(User)#4 (1) {
["id":"User":private]=>
string(7) "Dharman"
}
As you can see, the code created an instance of the class using late-static binding and it also assigned the value to a private property, which you can't do otherwise.
P.S. My example is a little bit tidier. I added parameter typing and removed a lot of unnecessary code. In particular, I remove empty try-catch which is a terrible practice.
I have now got this working, although I feel this is probably not the best way of doing it.
I'm primarily front end so please comment if there are improvements or best practices.
public static function findByQuery($sql,$bindChar = '',$bindVal = '') {
try {
$statement = Database::$connection->prepare($sql);
if(!empty($bindChar)) :
$statement->bind_param("$bindChar", $bindVal);
endif;
if($statement->execute()) :
$result = $statement->get_result();
$output = $result->fetch_object();
endif;
$statement->close();
if(!empty($output)) :
$class = get_called_class();
$object = new $class;
foreach(get_object_vars($output) as $key => $value) :
$object->$key = $value;
endforeach;
endif;
if(!empty($object)) :
return $object;
endif;
} catch(Exception $e) {
}
}
My initial thoughts were declaring an object and then I thought that the PHP fetch_object call would have just assigned my object it's properties after initiating the Class but that wasn't the case.
So what I've done is that if the statement is successful and a results object is created, I then get the object properties and values with the get_object_vars() command, and then loop through these as a key value pair, assigning each attribute it's returned value.
I can confirm this works as I can now run $admin->remove() from my removal script, as opposed to what I was having to do before which was Admin::remove($id);
function fbPageCounter()
{
$link = "https://graph.facebook.com/google?fields=name,likes&access_token=ID|SECRET";
$gData = file_get_contents($link);
$gData = json_decode($gData);
return $gData;
}
Without it being in a function I can echo it like this: $fbData->{'likes'}
Since it is now into a function, how do I echo the likes data?
UPDATE:
I'd like to know how to return the data by echoing it with the function like so: echo fbPageCounter(); not sure what goes inside to return the likes or name..
For a dynamic approach capable of accessing multiple properties, perhaps a nice solution is a class? Might be overkill but depending on what you need it to do, might not be?
class FbPageCounter {
// Stores json data from FB
public $gData;
// Set $gData
public function __construct() {
$link = "https://graph.facebook.com/google?fields=name,likes&access_token=ID|SECRET";
$gData = file_get_contents($link);
$this->gData = json_decode($gData);
}
// Returns likes
public function getLikes() {
return $this->gData->{'likes'};
}
// Returns name
public function getName() {
return $this->gData->{'name'};
}
}
$n = new FbPageCounter();
echo $n->getLikes();
echo $n->getName();
To ease up the debuging for my class(es) I want to bind a function to the status of other function events. The current set-up I have, is similair to following the code:
class config {
function __construct($file) {
$this->functions = array(); // The array with function run/succes information
if (!empty($file)) {
$this->checkFile($file);
}
}
public function checkFile($file) {
$this->functionStatus('run',true);
if (file_exists($file)) {
$this->functionStatus('succes',true);
return true;
} else {
$this->functionStatus('succes',true);
return false;
}
}
/* Simplified functionStatus function */
private function functionStatus($name,$value) {
/* - validations removed -*/
// Get parent function name
$callers = debug_backtrace();
$function = $callers[1]['function'];
/* - validations removed -*/
$this->functions[$function][$name] = $value;
}
}
An example of the principle in use:
$config = new config('example.ini');
print var_dump($config->functions);
/* Results in:
array(1) {
["checkFile"]=> array(2) {
["run"]=> bool(true)
["succes"]=> bool(true)
}
}
*/
While this set-up works fine. I would like to improve it by removing the manually placed $this->functionStatus('run',true) function everytime I create a function to keep the code a bit cleaner and prevent assuming a function isn't run because someone forgat defining the functionStatus at top of the function. Same goes for the definitions at the return events.
*Note, the best solution would also support this binding with other classes
Is there any way to accomplish this 'event binding' with PHP ?
You can do this using the __call magic method. Change all your public functios to private methods, and add a prefix to the name, e.g.
private function internal_checkFile($file) {
...
}
Then add the magic method:
public function __call($name, $arguments) {
$this->functionStatus('run', true);
return call_user_func_array(array($this, "internal_$name"), $arguments);
}
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.
I'm working on a class that will count twitter reactions to a link and also display them.
Currently I'm working on the counting portion and my count always equals 0 even though the array created in the constructor has multiple elements.
Any help would be appreciated. Thanks.
<?php
class TwitterReactions{
function __construct($url){
if($url){
$output=array();
$query = 'http://search.twitter.com/search.json?q='.$url;
$reactions=file_get_contents($query);
$reactions_array=json_decode($reactions, TRUE);
foreach($reactions_array as $results){
foreach($results as $result){
$output['user'][]=$result['from_user'];
$output['image'][]=$result['profile_image_url'];
$output['message'][]=$result['text'];
$output['date'][]=$result['created_at'];
}
}
return $output['user'];
} else {
echo "<p>Please provide a url...</p>";
}
}
function count_reactions($output){
//print_r($output);
$count = count($output['user']);
return $count;
}
}
?>
I agree with some of what Aliaksandr Astashenkau has in his answer but there are still some problems with the class.
It looks to me as if your initial problem was that you were expecting the __construct to return $output and you were then passing the object that you created into the count_reactions() method. Something like this...
$twitter = new TwitterReactions($url);
$count = $twitter->count_reactions($twitter);
You don't have your call to the count_reactions() method posted so this is just a hunch. If that's how you were using it the constructor it isn't meant to be used that way. Constructors always return a new instance of the class. You cannot return any other type of value from a constructor. You cannot use the return keyword in the __construct method.
As Aliaksandr Astashenkau points out $output should be a class member. I would also make $count a class member. In this case there's not much of a point of making either private so you don't really need accessor methods either but you can include them if you want.
I would make the class something like this...
<?php
class TwitterReactions
{
public $url = '';
public $output = array();
public $count = 0;
function __construct($url)
{
$this->url = $url;
$query = 'http://search.twitter.com/search.json?q='.$url;
$reactions=file_get_contents($query);
$reactions_array=json_decode($reactions, TRUE);
foreach($reactions_array as $results)
{
foreach($results as $key => $result)
{
// I find it easier if the data is arranged by each tweet but you can keep the array structure how you have it.
$this->output[$key]['user'] = $result['from_user'];
$this->output[$key]['image'] = $result['profile_image_url'];
$this->output[$key]['message'] = $result['text'];
$this->output[$key]['date'] = $result['created_at'];
}
}
$this->count = count($this->output);
}
}
You could then use the class like this
$twitter = new TwitterReactions($url);
// you now have access to output directly
$twitter->output;
// and count
$twitter->count;
Anyhow there are many ways to accomplish the same thing but I hop this helps give you some ideas.
You probably want to make an $output array to be a property of your class. Then $this->output would be availbale in count_reactions method.
<?php
class TwitterReactions {
public $output;
function __construct($url){
if($url){
$output=array();
$query = 'http://search.twitter.com/search.json?q='.$url;
$reactions=file_get_contents($query);
$reactions_array=json_decode($reactions, TRUE);
foreach($reactions_array as $results){
foreach($results as $result){
$output['user'][]=$result['from_user'];
$output['image'][]=$result['profile_image_url'];
$output['message'][]=$result['text'];
$output['date'][]=$result['created_at'];
}
}
$this->output = $output;
return $output['user'];
} else {
echo "<p>Please provide a url...</p>";
}
}
function count_reactions($output){
//print_r($this->output);
$count = count($this->output['user']);
return $count;
}
}