im trying to create a class to manage widgets. I have problems with a protected data in parent class:
Widget.php
/** Parent class **/
class Widget{
protected $html =""; //formated html data
// method to load views in {system_path}/widgets/{widget_name}/views/
protected function LoadView($filename){
if(!empty($filename) && is_string($filename)){
$output = "";
$dir = WIDGET_PATH . "views" . DS . $filename;
ob_start();
include($dir);
$output = ob_get_contents();
ob_end_clean();
return $output;
}
return NULL;
}
//method to render formated html data
public function Render(){
if(isset($this->html) && !empty($this->html)){
return $this->html;
}
return NULL;
}
//static method to load a Widget
public static function Load($widgetName){
if(!empty($widgetName) && is_string($widgetName)){
$widgetName = strtolower($widgetName);
if(file_exists(WIDGET_PATH . $widgetName . DS . $widgetName . ".php")){
include_once(WIDGET_PATH . $widgetName . DS . $widgetName . ".php");
if(class_exists($widgetName."_Widget")){
$class = $widgetName."_Widget";
return new $class();
}
}
}
return FALSE;
}
}
/widgets/socialbar.php
/** SocialBar Widget **/
class Socialbar_Widget extends Widget
{
public function __construct(){
$this->html = "demo"; // test to see if it works
}
}
index.php
/*load class files, etc */
$Social = Widget::Load("socialbar"); //works, perfectly loads Socialbar_Widget()
var_dump($social); // works : object(Socialbar_Widget)[29] protected html = 'demo' ......
$Social->Render(); // throws Fatal error: Using $this when not in object context
To extend a variable inside parent class should i use "public"? Or what i mistake.
Thanks for help guys.
Your class name is class Socialbar_Widget,
Your are calling it in lower case
$Social = Widget::Load("socialbar")
and in load method you are doing strtolower($widgetName).
Check class file name.php. Load function may have returning false.
Related
I have this file root/core/Router.php
<?php
namespace Core;
class Router {
protected $url;
protected $controller;
private function parseURL() {
// threat the $this->url; for example ["r", "product"]
}
private function request() {
$this->controller = Controller::get($this->url[1]);
}
public function __construct() {
$this->parseURL();
$this->request();
}
}
?>
then file root/core/Controller.php
<?php
namespace Core;
class Controller {
public static function model($name, $params = []) {
$model = "\\Model\\$name";
return new $model($params);
}
public static function view($name, $params = []) {
require_once APP_DIR . "view/" . $name . ".php";
}
public static function get($name, $params = []) {
require_once APP_DIR . "controller/" . $name . ".php";
$name = "\\Controller\\$name";
return new $name($params);
}
}
?>
then root/controler/Product.php
<?php
namespace Controller;
use Core\Controller;
use Model\Product;
class Product {
public function get() {
$ret['state'] = 510;
$productModel = new Product;
$products = $productModel->getAll();
if(isset($products)) {
$ret['products'] = $products;
$ret['state'] = 200;
}
return $ret;
}
}
?>
then file root/model/Product.php
<?php
namespace Model;
class Product {
public function add($values) {
return Database::insert("product", $values);
}
}
?>
and root/core/Model.php
<?php
namespace Core;
class Model {
protected $table = null;
public function getAll() {
// some code to collect data
}
}
?>
What i want to achive is that every Controller in root/controller/*.php able to load any Model in root/model/*.php but class inside root/model/*.php must able to access (inheritance/extends) the Model class inside root/core/Model.php i firstly asked on chatGPT for some AI Generated answer, that the reason why i get this far.
Then i get this error, when the AI keep giving the same answer.
Fatal error: Cannot declare class Controller\Product because the name is already in use in C:\xampp\htdocs\app\shop\controller\Product.php on line 6
I actually realize that the simple way probably with naming the class so ther no conflict between it but i became aware how to properly using the namespace if its such features in php. Those files loaded without any autoloader, so i just require_once each file in root/init.php file.
I read few documentations but hard to implement in multiple files and directorys.
I Apreciate any feedback, thanks
I'm creating a simple PHP MVC system based on the "original" MVC principle, that is...
Controller instructs the model based on user input / queries and loads view
Model is a data layer is is unaware and independant from the controller or view
View creates own copy of data from Model for presentaion purposes
Everything I've done so far conforms to these principles, however, I've stumbled upon a problem as follows...
I set a default page title in the parent model
I inform the model to change its page title value using a model method based on the user page request in the correct controller method (index_controller/index)
I make a copy of models page title in the home view to b used on the template.
The problem is, the page title isnt updated because im setting the view variable in the index view constructor which happens before the model can be updated by the controller.
I can get the correct view variable by fetching and assigning it just before load the template, but the template loading is done in the parent view class which is an issue because different views require different variables.
Please let me know what I'm doing wrong! Any help is much appreciated!
It might help to see some of thecode in question...
lib/bootstrap.php
class Bootstrap {
function __construct() {
// Gets the request URL and seperate into array
$this->url = $this->getUrlArray();
// Generates the controller name based on URL array
$this->controller_name = $this->getControllerName();
// Loads controller file and creates new controller object
$this->controller = $this->getController();
// Calls requested methods based on URL array
$this->callMethods();
}
function getUrlArray() {
$url = isset($_GET['url']) ? rtrim($_GET['url'], '/') : 'index';
$url = explode('/', $url);
return $url;
}
function getControllerName() {
return $this->url[0] . '_controller';
}
function getController() {
$file = 'controllers/' . $this->controller_name . '.php';
if(file_exists($file)) {
require $file;
return new $this->controller_name($this->url[0]);
} else {
require 'controllers/error_controller.php';
return new Error_Controller('error');
}
}
function callMethods() {
if(!isset($this->url[1])) {
$this->controller->index();
} else {
$method_name = $this->url[1];
if(method_exists($this->controller, $method_name)) {
if(!isset($this->url[2])) {
$this->controller->$method_name();
} else {
$this->controller->$method_name($this->url[2]);
}
} else {
$this->controller->index();
}
}
}
}
lib/controller.php
class Controller {
protected $model;
public function __construct($name) {
$this->base_name = $name;
$this->model_name = $this->getModelName();
$this->model = $this->getModel();
$this->view_name = $this->getViewName();
$this->view = $this->getView();
//$this->creatView();
}
public function getModelName() {
return $this->base_name . '_model';
}
public function getViewName() {
return $this->base_name . '_view';
}
public function getModel() {
$file = 'models/' . $this->model_name . '.php';
if(file_exists($file)) {
require $file;
return new $this->model_name();
} else {
die('ERROR: This page is missing a model!');
}
}
public function getView() {
$file = 'views/' . $this->view_name . '.php';
if(file_exists($file)) {
require $file;
return new $this->view_name($this->model);
} else {
die('ERROR: This page is missing a view!');
}
}
}
lib/model.php
class Model {
public function __construct() {
$this->page_title = SITE_NAME;
}
public function setPageTitle($pre, $seperator) {
$this->page_title = $pre . ' ' . $seperator . ' ' . SITE_NAME;
}
}
lib/view.php
class View {
protected $model;
public function __construct(Model $model) {
$this->model = $model;
}
public function output($name, $noInclude = false) {
$file = 'templates/' . $name . '.php';
if(file_exists($file)) {
if($noInclude) {
require 'templates/' . $name . '.php';
} else {
require 'templates/header.php';
require 'templates/' . $name . '.php';
require 'templates/footer.php';
}
} else {
die('ERROR: This page is missing a template!');
}
}
}
controllers/index_controller.php
<?php
class Index_Controller extends Controller {
function __construct($name) {
parent::__construct($name);
}
function index() {
$this->model->setPageTitle('Home', '-');
var_dump($this);
$this->view->output('index/index');
}
function test($value = 'not set') {
echo 'You are in test and the value is ' . $value;
}
}
views/index_view.php
class Index_View extends View {
public function __construct(Model $model) {
parent::__construct($model);
$this->page_title = $this->model->page_title;
}
}
models/index_model.php
class Index_Model extends Model {
public function __construct() {
parent::__construct();
}
}
templates/header.php
<!DOCTYPE html>
<html>
<head>
<title><?php echo $this->page_title; ?></title>
</head>
<body>
<div id="header">
<!-- -->
</div>
<div id="content">
My var_dump in index_controller looks like this...
Cheers,
Tom
i am passing function _homepage to index but it does not pass the $data['articles'] with it.
can any one help me to solve this?
this code is updated
public function index(){
$mydata = array();
$this->mydata['menu'] = $this->Mdl_page->get_nested();
$this->mydata['page'] = $this->Mdl_page->get_by(array('slug' => (string) $this->uri->segment(1)), TRUE);
count($this->mydata['page']) || show_404(current_url());
//fetch page data
$method = '_' . $this->mydata['page']->template;
if (method_exists($this, $method)) {
$this->$method();
}
else {
log_message('error', 'Could not load template ' . $method .' in file ' . __FILE__ . ' at line ' . __LINE__);
show_error('Could not load template ' . $method);
}
$this->mydata['subview'] = $data['page']->template;
//dump($data);
$this->load->view('_main_layout', $this->mydata);
}
private function _page(){
dump('Welcome from the page template');
}
private function _homepage(){
$this->load->model('admin/Mdl_Articles');
$this->db->limit(6);
$this->mydata['articles'] = $this->Mdl_Articles->get();
//dump($data['articles']);
}
private function _news_archive(){
dump('Welcome from the newspage template');
}
You should instead create a private array property
private mydata = array();
In your _homepage method, contents should be:
private function _homepage(){
// ...
// target your class scope property
$this->mydata['articles'] = $this->Mdl_Articles->get();
and change your all $data variable to $this->mydata in index method and pass it to the view like this:
$this->load->view('_main_layout', $this->mydata);
I am writing my own MVC framework and has come to the view renderer. I am setting vars in my controller to a View object and then access vars by echo $this->myvar in the .phtml script.
In my default.phtml I call the method $this->content() to output the viewscript.
This is the way I do it now. Is this a proper way to do that?
class View extends Object {
protected $_front;
public function __construct(Front $front) {
$this->_front = $front;
}
public function render() {
ob_start();
require APPLICATION_PATH . '/layouts/default.phtml' ;
ob_end_flush();
}
public function content() {
require APPLICATION_PATH . '/views/' . $this->_front->getControllerName() . '/' . $this->_front->getActionName() . '.phtml' ;
}
}
Example of a simple view class. Really similar to yours and David Ericsson's.
<?php
/**
* View-specific wrapper.
* Limits the accessible scope available to templates.
*/
class View{
/**
* Template being rendered.
*/
protected $template = null;
/**
* Initialize a new view context.
*/
public function __construct($template) {
$this->template = $template;
}
/**
* Safely escape/encode the provided data.
*/
public function h($data) {
return htmlspecialchars((string) $data, ENT_QUOTES, 'UTF-8');
}
/**
* Render the template, returning it's content.
* #param array $data Data made available to the view.
* #return string The rendered template.
*/
public function render(Array $data) {
extract($data);
ob_start();
include( APP_PATH . DIRECTORY_SEPARATOR . $this->template);
$content = ob_get_contents();
ob_end_clean();
return $content;
}
}
?>
Functions defined in the class will be accessible within the view like this:
<?php echo $this->h('Hello World'); ?>
Here's an example of how i did it :
<?php
class View
{
private $data = array();
private $render = FALSE;
public function __construct($template)
{
try {
$file = ROOT . '/templates/' . strtolower($template) . '.php';
if (file_exists($file)) {
$this->render = $file;
} else {
throw new customException('Template ' . $template . ' not found!');
}
}
catch (customException $e) {
echo $e->errorMessage();
}
}
public function assign($variable, $value)
{
$this->data[$variable] = $value;
}
public function __destruct()
{
extract($this->data);
include($this->render);
}
}
?>
I use the assign function from out my controller to assign variables, and in the destructor i extract that array to make them local variables in the view.
Feel free to use this if you want, i hope it gives you an idea on how you can do it
Here's a full example :
class Something extends Controller
{
public function index ()
{
$view = new view('templatefile');
$view->assign('variablename', 'variable content');
}
}
And in your view file :
<?php echo $variablename; ?>
I have the following situation.
I have a class with a lot of functions. Each function starts with executing the same method. Is there a way that I can like implement this method into the function so that it is executed automatically?
Here is an example:
class test
{
static function_1($param) {some_method($param); other stuff....}
static function_2($param) {some_method($param); other stuff then function 1....}
static function_3($param) {some_method($param); other stuff then function 1 and function 2....}
}
So is there a way to execute some_method(); automaticly without declaring it in each function?
Thanks in advance!
Whole code:
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/*
* The Assets Library
*
* This class let's you add assets (javascripts, stylesheets and images) way easier..
*/
class Assets {
private $css_url;
private $js_url;
private $img_url;
public function __construct()
{
$CI =& get_instance();
$CI->config->load('assets');
$asset_url = base_url() . $CI->config->item('assets_dir');
$this->css_url = $asset_url . $CI->config->item('css_dir_name');
$this->js_url = $asset_url . $CI->config->item('js_dir_name');
$this->img_url = $asset_url . $CI->config->item('img_dir_name');
}
// Returns the css html link
public function css_html_link($filename)
{
// Check whether or not a filetype was given
$filename = $this->_add_filetype($filename, 'css');
$link = '<link type="text/css" rel="stylesheet" href="' . $this->css_url . $filename . '" />';
return $link;
}
// Returns the css link
public function css_link($filename)
{
$filename = $this->_add_filetype($filename, 'css');
return $this->css_url . $filename;
}
// Returns the js html link
public function js_html_link($filename)
{
$filename = $this->_add_filetype($filename, 'js');
$script = '<script type="text/javascript" src="' . $this->js_url . $filename . '"></script>';
return $script;
}
// Return the js link
public function js_link($filename)
{
$filename = $this->_add_filetype($filename, 'js');
return $this->js_url . $filename;
}
// Returns the image html tag
public function img_html_link($filename, $rel = NULL)
{
// Get the filename without the filetype
$alt_text = substr($filename, 0, strpos($filename, '.')+1);
$alt_text = 'alt="'.$alt_text.'"';
// If relation is giving, use it
$img_rel = ($rel !== FALSE) ? 'rel="' . $rel . '"' : '';
$image = '<img src="' . $this->img_url . $filename . '" '.$rel.' ' . $alt_text . '/>';
return $image;
}
// Return the image link
public function img_link($filename)
{
return $this->img_url . $filename;
}
// Check whether or not a filetype was specified in $file, if not, it will be added
private function _add_filetype($file, $type)
{
if(strpos($file, '.' . $type) === FALSE)
{
$file = $file . '.' . $type;
}
return $file;
}
}
/* End of file assets.php */
/* Location: ./application/libraries/assets.php */
every time you initiate the class, it calls the __construct() function, or in PHP 4 (I hope you are not using php 4) it uses the function with the same name as the class
If you do this, it should work for every initiate of the class:
function __construct($param){
some_method($param);
}
if you call multiple functions in the same initiation of the class, you could do this:
var $param;
function __construct($param){
$this->param = $param;
}
function doMethod(){
some_method($this->param);
}
function function_1()
{
$this->doMethod();
}
Calling the class multiple times, with different params. Perhaps try this approach:
function __call($function, $param){
some_method($param);
switch ($function){
case 'function1':
$this->function1($param);
break;
/// etc..
}
}
I'm afraid that in this case the answer is 'no'.
You're not 'declaring' some_method() each time, you are calling it. If you don't call it, it can't run, so you have to call it each time.
Cut & paste.....
Why not paste your actual code here, some refactoring may help.
Edit after seeing actual code
I can't see the problem with your existing code. It is clear and you will know what it does in a year's time. I would keep it as it is. The answer you accepted will work, but it is obfuscating your code. You will have problems working out what you did and why you did it in when you come back to maintain your code in the future.
You could create a class containing an instance of the class test (composition) and implement its __call magic method. Something akin to:
class testWrapper
{
private $test;
function __construct()
{
$this->test = new Test();
}
function __call($name, $args)
{
call_user_func_array(array($this->test, 'some_method'), $args);
call_user_func_array(array($this->test, $name), $args);
}
}
You then call methods from the test class on the instance object of testWrapper.
You can further refine the logic in the __call method to only call some_method() based on the passed-in method name, etc.