Weird Error with certain filename in php - php

I am having a really weird error, I have this template Engine (which I think is working correctly since it shows most files correctly):
<?php
class Template
{
protected $template;
protected $vars = array();
public function __construct($template)
{
$this->template=$template;
}
public function __get($key)
{
// TODO: Implement __get() method.
return $this->vars[$key];
}
public function __set($key, $value)
{
// TODO: Implement __set() method.
$this->vars[$key]=$value;
}
public function __toString()
{
extract($this->vars);
chdir(dirname($this->template));
ob_start();
include basename($this->template);
//echo $this->template;
return ob_get_clean();
}
}
Then I have this file named topic.php with the following code in it:
<?php
require ('core/init.php');
$template = new Template("templates/topic.php");
echo $template;
?>
And my template "templates/topic.php" looks like this:
<?php
echo "Testo";
And finally I have this other file "templates/test.php" which looks like this:
<?php
echo "Testing";
If in line two of topic.php I change $template = new Template("templates/topic.php"); to $template = new Template("templates/test.php");
It just prints correctly, "Testing" shows in the webpage but if I want to show "templates/topic.php" it doesn't show anything in the webpage. Also if I refactor "templates/topic.php" to "templates/topici.php" for example, it works fine.
This is not a project I need to deliver and the workaround is really simple... is not to use the file name topic.php in the template directory, but I want to understand what is happening.
Note: Also, I have other files with a similar structure where for example the file is named "create.php" and the file it is supposed to show is "templates/create.php" and they all work fine.
Thanks for your help.

Related

calling public function from another php

I'm working on WordPress plugin where I have 2 php files.
class.php has code as:
class Plugin_Name {
public function say_hello() {
echo "Hello";
}
}
Now I want to call this say_hello() function from another welcome.php file.
I tried
$hello_function = new Plugin_Name();
$hello_function->say_hello();
But it doesn't work. Is there any way to call public function from another php ?
You need to include the first function in the other file, that way the other file knows the code is there. At the top of welcome.php add
require_once('/path/to/class.php');
$hello_function = new Plugin_Name();
$hello_function->say_hello();

How to create global function in opencart

I'm trying to create simple function that can be executed on any page.
function something() {
$string = 'Hello World';
return $string;
}
Let's say I'm in the category page, I would just call $a = something(); and it would return my value
Platform : OpenCart
P.S. I'm still studying MVC architecture
Since you are wanting to understand and learn about the MVC system, the correct way to go about this would be to create your own helper file and put it in /system/helper/ and then add the helper to system/startup.php. Take a look at how the json.php / utf8.php are done in these as a guide
Create a new file to system/library/yourclassname.php with same code.
Also please add a class name to your function as below:
class yourclassname {
function something() {
$string = 'Hello World';
return $string;
}
}
And add it to your index.php file as below;
$registry->set('yourclassname', new yourclassname($registry));
Finally add it to your startup.php file as below:
require_once(DIR_SYSTEM . 'library/yourclassname.php');
You can use it anywhere with $this->yourclassname->something();
Thats All..
You can create a function in any opencart library (system/library/)
As example in system/library/document.php
function something() {
$string = 'Hello World';
return $string;
}
And use anywhere in openсart as
$something=$this->document->something();
P/s code in header.tpl will not work in ajax or direct request
Matricore's answer worked perfectly for me, but I also had to construct the new class with the config and db available.
Code below:
class yourclassname {
public function __construct($registry) {
$this->config = $registry->get('config');
$this->db = $registry->get('db');
}
}
You can then run queries via $this->db->query("Your query here");

include view after extracting data

I have two functions loadView() and render() in my view class.
public function loadView($view){
if(file_exists(APP.'view/'.$view)){
require(APP.'view/'.$view);
}
else{
die(APP.'view/'.$view.' not found.');
}
}
public function render($view,$data = array()){
if(!empty($data)){
extract($data);
ob_start();
//$this->loadView($view); -------------- not woriking
require(APP.'view/'.$view); ------ working
$this->output = ob_get_clean();
echo $this->output;
}
}
Whenever I call the loadview function from the render its not working. But if I include the view file directly after extracting data, it works. Anybody can tell me Why is this happening here or any solution ?
This will not work, because require() includes the required code in the place of the require() statement and in the scope of that location. In this case, the scope is the loadView() method. Within that method scope the extracted variables from the render() method are not available.
Instead of finding a solution for that, I would encourage you to use a template engine for your views like Twig. This is standard practice in MVC frameworks. Then, you could do something like this:
public function render($view,$data = array()){
$template = $twig->loadTemplate($view);
echo $template->render($data);
}

Going from the Controller to the View

I am working on creating my own very simple MVC and I am brainstorming ways to go from the controller to the view. Which involves sending variables from a class to just a plain old PHP page.
I am sure that this has been covered before, but I wanted to see what kind of ideas people could come up with.
//this file would be /controller/my_controller.php
class My_Controller{
function someFunction(){
$var = 'Hello World';
//how do we get var to our view file in the document root?
//cool_view.php
}
}
Some kind of hashtable is a good way to do that. Return your variables as association array which will fill all the gaps in your view.
Store your variables as a property in your controller object, then extract them when rendering
class My_Controller {
protected $locals = array();
function index() {
$this->locals['var'] = 'Hello World';
}
protected function render() {
ob_start();
extract($this->locals);
include 'YOUR_VIEW_FILE.php';
return ob_get_clean();
}
}
You can define those magic __get and __set methods to make it prettier
$this->var = 'test';
I'm also developing my own simple MVC and the most simple way to do it is ...
class My_Controller
{
function someFunction() {
$view_vars['name'] = 'John';
$view = new View('template_filename.php', $view_vars);
}
}
View class
class View
{
public function __construct($template, $vars) {
include($template);
}
}
template_filename.php
Hello, <?php echo $vars['name'];?>
I highly recommend you to take a look at PHP Savant http://phpsavant.com/docs/
I'd checkout Zend_View and how it accomplished view rendering.
You can get the source of View and AbstractView on github - unfortunaly I don't find the current repository (in svn) that easy to browse.
Essentially the view variables are contained in a View object (which your controller would have access to), then the template (plain old php document) is rendered inside that object. That method allows the template access to $this.
It would be something like:
<?php
class View
{
public function render()
{
ob_start();
include($this->_viewTemplate); //the included file can now access $this
return ob_get_clean();
}
}
?>
So in your controller:
<?php
class Controller
{
public function someAction()
{
$this->view->something = 'somevalue';
}
}
?>
And your template:
<p><?php echo $this->something;?></p>
In my opinion this pattern allows you much flexibility with the view.
I created my own MVC for the free PHP course I'm conducting for a handful of people wanting to get better at PHP.
By far the best way to do this is to use the Command + Factory pattern.
E.g.
interface ControllerCommand
{
public function execute($action);
}
In each controller:
class UserController implements ControllerCommand
{
public function execute($action)
{
if ($action == 'login')
{
$data['view_file'] = 'views/home.tpl.php';
}
else if ($action == 'edit_profile')
{
$data['view_file'] = 'views/profile.tpl.php';
$data['registration_status'] = $this->editProfile();
}
return $data;
}
}
From your main front controller:
$data = ControllerCommandFactory::execute($action);
if (!is_null($data)) { extract($data); }
/* We know the view_file is safe, since we explicitly set it above. */
require $view_file;
The point is that every Controllercommand class has an execute function and that returns its view and any data for that view.
For the complete MVC, you can access the open source app by emailing me at theodore[at]phpexperts.pro.

Templating system scoping issue

I'm trying to whip up a skeleton View system in PHP, but I can't figure out how to get embedded views to receive their parent's variables. For example:
View Class
class View
{
private $_vars=array();
private $_file;
public function __construct($file)
{
$this->_file='views/'.$file.'.php';
}
public function set($var, $value=null)
{
if (is_array($var))
{
$this->_vars=array_merge($var, $this->_vars);
}
else
$this->_vars[$var]=$value;
return $this;
}
public function output()
{
if (count($this->_vars))
extract($this->_vars, EXTR_REFS);
require($this->_file);
exit;
}
public static function factory($file)
{
return new self($file);
}
}
test.php (top level view)
<html>
<body>
Hey <?=$name?>! This is <?=$adj?>!
<?=View::factory('embed')->output()?>
</body>
</html>
embed.php (embedded in test.php
<html>
<body>
Hey <?=$name?>! This is an embedded view file!!
</body>
</html>
Code:
$vars=array(
'name' => 'ryan',
'adj' => 'cool'
);
View::factory('test')->set($vars)->output();
Output:
Hey ryan! This is cool! Hey [error for $name not being defined]
this is an embedded view file!!
The problem is the variables I set in the top level view do not get passed to the embedded view. How could I make that happen?
So, I'm not exactly answering your question, but here's my super-simple hand-grown template system. It supports what you're trying to do, although the interface is different.
// Usage
$main = new SimpleTemplate("templating/html.php");
$main->extract($someObject);
$main->extract($someArray);
$main->name = "my name";
$subTemplate = new SimpleTemplate("templating/another.php");
$subTemplate->parent($main);
$main->placeholderForAnotherTemplate = $subTemplate->run();
echo $main; // or $main->run();
// html.php
<html><body><h1>Title <?= $name ?></h1><p><?= $placeHolderForAnotherTemplate ?></p></body></html>
<?php
// SimpleTemplate.php
function object_to_array($object)
{
$array = array();
foreach($object as $property => $value)
{
$array[$property] = $value;
}
return $array;
}
class SimpleTemplate
{
public $source;
public $path;
public $result;
public $parent;
public function SimpleTemplate($path=false, $source=false)
{
$this->source = array();
$this->extract($source);
$this->path($path);
}
public function __toString()
{
return $this->run();
}
public function extract($source)
{
if ($source)
{
foreach ($source as $property => $value)
{
$this->source[$property] = $value;
}
}
}
public function parent($parent)
{
$this->parent = $parent;
}
public function path($path)
{
$this->path = $path;
}
public function __set($name, $value)
{
$this->source[$name] = $value;
}
public function __get($name)
{
return isset($this->source[$name]) ? $this->source[$name] : "";
}
public function mergeSource()
{
if (isset($this->parent))
return array_merge($this->parent->mergeSource(), $this->source);
else
return $this->source;
}
public function run()
{
ob_start();
extract ($this->mergeSource());
include $this->path;
$this->result = ob_get_contents();
ob_end_clean();
return $this->result;
}
}
well, you create a new instance of the class, so there are no variables defined in the embedded template. you should try to copy the object, rather than creating a new one.
edit: I'm talking about the factory method
The main issue is that your views have no direct knowledge of each other. By calling this:
<?=View::factory('embed')->output()?>
in your "parent" view, you create and output a template that has no knowledge of the fact that it is inside another template.
There are two approaches I could recommend here.
#1 - Associate your templates.
By making your embedded templates "children" of a parent template, you could allow them to have access to the parent's variables at output() time. I utilize this approach in a View system I built. It goes something like this:
$pView = new View_Parent_Class();
$cView = new View_Child_Class();
$pView->addView($cView);
At $pview->render() time, the child view is easily given access to the parent's variables.
This method might require a lot of refactoring for you, so I'll leave out the dirty details, and go into the second approach.
#2 - Pass the parent variables
This would probably be the easiest method to implement given the approach you've taken so far. Add an optional parameter to your output method, and rewrite it slightly, like this:
public function output($extra_vars = null)
{
if (count($this->_vars))
extract($this->_vars, EXTR_REFS);
if (is_array($extra_vars)) extract($extra_vars, EXTR_REFS);
require($this->_file);
exit;
}
If you add a simple getter method as well:
public function get_vars()
{
return $this->_vars;
}
Then you can embed your files with what is effectively read-access to the parent's variables:
<?=View::factory('embed')->output($this->get_vars())?>
$this will be a reference to the current template, ie. the parent. Note that you can have variable name collisions via this method because of the two extract calls.
You could make your $_vars property static, not particularly elegant, but would work for what you are trying to achieve.
On a side note... your array_merge() in the set() function is wrong, swap your 2 variables around.

Categories