I have a php script that receives variables via GET method. I can access the variables using $_GET['var'], but I want to use ZF2's $this->params()->fromQuery('var') construct, without having ZF2 engine in place.
I am interested in 'how'. In case you are interested in 'why (would I want to do this)', it's because I am working on large existing legacy codebase, rewriting it to use ZF2 step by step. Next step is to use params().
I am thus interested in building up the needed code to make the params() plugin work. My thoughts are to write a trait that can be called into my class, where then inside the class I can use $this->params()->fromQuery().
My current thoughts
trait ParamTrait
{
public function params(string $param = null, mixed $default = null)
{
//magic
return $params;
}
}
class X
{
use ParamTrait;
function showGet()
{
echo $this->params()->fromQuery('var');
}
}
The magic part is what I am looking for to fill.
params() is just a controller plugin proxy to Zend\Http\Request
You can use this anywhere outside of a full ZF2 MVC app by adding zendframework/zend-http to your composer.json
require{
"zendframework/zend-http": "2.3.0"
}
include the ./vendor/autoload.php in your file
<?php
require 'vendor/autoload.php';
$request = new \Zend\Http\PhpEnvironment\Request();
//post
$post = $request->getPost();
//query
$query = $request->getQuery();
// etc, etc...
Related
I've been searching and reading a lot about how is the best way (code-wise) to get application's config variables in a PHP environment. After that I've sum up that are two more generally used ways to manage with configuration files and variables.
But I'm a bit confused about it, one, method 1, is using an static class. The other one, method 2 is using an instantiable class.
First one is worse to unit testing than second one. And It's similar to a global variable. Isn't it?
Second one need a global varaible in order to use instantiated object.
I'll try to explain myself.
Facts:
- App's settings are kept on a INI file.
- This INI file has sections, in order to maintain configuration variables.
- I've got only one INI file.
- Class do some validation of configuration file.
- Code examples below aren't complete, it's only a sample to ilustrate my question.
Method 1: Using static class
This method use a Config static class, it uses static because only one Config object would be used in all application.
Code example:
class Config
{
static private $data;
static public function load($configFile) {
self::$data = parse_ini_file($configFile, true, INI_SCANNER_RAW)
}
static public get($key) {
// code stuff to deal with sections and keys, but basically
return self::$data[$key];
}
}
On my application I create the static object, once, using this code:
\Config::load('/where/is/my/ini/file.ini');
In this case, every time i want to get a value i use:
$host = \Config::get('database.host');
function example()
{
echo \Config::get('another.value');
}
Method 2: Using instable object
In this scenario I use a Config class object.
Code example:
class Config {
private $data = array();
public function __construct($configFile) {
$this->data = parse_ini_file($configFile, true, INI_SCANNER_RAW)
}
public function get($key) {
// code stuff to deal with sections and keys, but basically
return $this->data[$key];
}
public function __get($key) {
return $this->get($key);
}
}
To use it, first we need to instantiate an object and then get the value:
$settings = new \Config('/where/is/my/ini/file.ini');
$host = $settings->get('database.host');
echo $settings->database->host;
But when I need this value inside a function, I need to use a global variable, which I think isn't right at all:
global $settings;
$settings = new \Config('/where/is/my/ini/file.ini');
function example()
{
global $settings;
echo $settings->get('another.value');
}
What I miss leading?
Thanks in advance to read and answer my question.
Simply you may also use a php file to keep your configurations for example, config.php and then you may use require from anywhere to get it:
// config.php
<?php
return array(
'database' => 'mysql',
'pagination' => array(
'per_page' => 10
)
);
Then use:
$config = require "path/to/config.php";
print_r($config);
You may use this with a function too, for example:
function someFunc()
{
$config = require "path/to/config.php";
// Now use it
}
You may create a class to get the the configurations using a method like, for example:
class Config {
static function get()
{
$config = require "path/to/config.php";
return $config;
}
}
So you may use:
$config = Config::get();
This is just another simple idea, you may extend it to use it like:
$perPage = Config::get('pagination.per_page');
Just need to add some more code to make it working like this way, give it a try.
Update:
Btw, I built a package named IConfig for my own MVC Framework back in 2013, it's also available on Packagist. You may use it or check it's source code, maybe you'll get better idea and can build a better one. But probably there are a lot better ones available on the web as well.
I think the problem you're trying to solve isn't really specific to PHP and either of the two techniques you've described could be viable ways to handle global configurations.
Having said that, I agree with Rangad, the solution is to use Dependency Injection. At it's simplest this pattern just means passing dependency's to a class/object/function as arguments. For example,
class Thing () {
private $host = '';
function __contructor($host) {
$this->host = $host;
}
function getHost()
{
echo $this->host;
}
}
$thing = new Thing( Config::get('database.host') );
$thing->getHost();
$thing2 = new Thing (Config::get('someOtherDatabase.host') );
$thing2.getHost();
This encapsulates your classes. Now they can be used in tests or even other applications so long as the needed dependences can be provided.
The nice thing about this is that you can use it with either of your proposed config options and probably others. For example if you're looking for something simple there's Pimple, a PHP Dependency Injection container written by the creator of the Symphoy PHP framework that in my opinion is great for smaller PHP projects http://pimple.sensiolabs.org/.
I have created a class of my own which I'm using from methods which override those in a couple of Cake's caching-related classes in order to customise the filenames of the files that CacheHelper saves. (The filenames sometimes need to include the type of user so that logged in users don't see cached versions of what non-logged in users would see and vice-versa, for example.)
My class is called MyCacheUtility and resides in app/Lib/Cache.
In this class, I need to ascertain what the controller and action are. What is the best way of doing so, and can I use Cake's CakeRequest class?
So far, I have included App::uses('CakeRequest', 'Network');, and...
I can't use $this->params('controller') as my class isn't a controller or component:
Error: Using $this when not in object context
File: /srv/www/app/Lib/Cache/MyCacheUtility.php
Line: 20
I can't use CakeRequest::param('controller') as (if I understand correctly) CakeRequest's methods are not designed to be used statically:
Error: Using $this when not in object context
File: /srv/www/cakephp/lib/Cake/Network/CakeRequest.php
Line: 864
The contents of my class:
App::uses('CakeRequest', 'Network');
class MyCacheUtility {
/**
* Sets the cache file’s filename to include references to user type
* and organisation (depending on the current controller and action)
* so that both logged-in users from different organisations and visitors
* get the right stuffs.
*/
public static function getPrefix() {
$prefix = '';
switch ('controller') . '/' . 'action') { // this is where I need the controller and action
case 'user_lists/organisation_lists':
$prefix .= MyCacheUtility::sessionDataToFilename('currentUser.organisationName');
break;
}
$prefix .= MyCacheUtility::sessionDataToFilename('currentUser.type');
return $prefix;
}
/**
*
*/
private static function sessionDataToFilename($sessionValue) {
if (CakeSession::read($sessionValue)) {
return strtolower(Inflector::slug(CakeSession::read($sessionValue))) . '-';
}
}
}
Update:
I found I wasn't able to pass the CakeRequest object from CacheDispatcher to MyCacheUtility class, and I think this is because CakeRequest has not been instantiated at the point at which CacheDispatcher does its stuff?
I do now have what I set out to do working. I'm passing Cake's $path variable through to my method and manually doing some processing based on this:
class MyCacheUtility {
public static function getPrefix($path) {
$pathParts = explode('/', parse_url($path, PHP_URL_PATH));
$prefix = '';
if (count($pathParts) > 1) {
if ($pathParts[1] == 'shared_lists') {
$prefix .= MyCacheUtility::sessionDataToFilename('currentUser.organisationName');
}
}
$prefix .= MyCacheUtility::sessionDataToFilename('currentUser.type');
return $prefix;
}
private static function sessionDataToFilename($sessionValue) {
if (CakeSession::read($sessionValue)) {
return strtolower(Inflector::slug(CakeSession::read($sessionValue))) . '-';
}
}
}
I was hoping to be able to directly apply Cake's own logic, but this seems to be doing the job okay so far.
Why do you need a library class at all? Extend the CacheHelper and use your customized cache helper via aliasing as $this->Cache in the views. To modify the file names it should be enough to override CacheHelper::_writeFile().
If you want to modify the caching itself write a new cache engine or extend an existing one to modify it's behavior. Your question does not really contain much information about why and what exactly you want to do so I can't give a better advice.
The CakeRequest object should be nearly everywhere present, so just pass it to the constructor of your cache lib. But like I said, architecturewise I doubt your approach is the best path to go. Show your cache lib code?
new MyCacheLib(CakeRequest $request);
Edit:
Write your custom Cache dispatcher or extend the existing one instead. You'll get the request object passed to it by default. Take a look at the CacheDispatcher filter. No need for a lib class here.
A more simple approach might be to write a custom cache engine and configure the helper to use that, same for the cache dispatcher filter. This way you can share the code. Or use a trait (php 5.4) to share the path building logic between the filter and the helper.
I would like to implement controllers that connect to any specific views like MVC does. I'm not using any framework that provided in PHP.
So, I need some guide and advice on doing it.
I have some controllers and views. For my views,i would like to just output my data only.
My concern now is how my function (like create() ) in controllers, can get all the $_POST['params'] that users input data in my views/create.php, and create a new Model in the create() controllers's function.
So,right now, i'm thinking to do in this way, I will create MyViews class in my controllers folder. The purpose is loading the specific views and get all the $_POST params into an object. Then, every controllers like Users_controllers, will create MyViews. In the function of Users_controllers, like create(), destroy(), I might use the function in MyViews to load specific views to load the object.
I found a source that load views
<?php
class MyView {
protected $template_dir = 'templates/';
protected $vars = array();
public function __construct($template_dir = null) {
if ($template_dir !== null) {
// Check here whether this directory really exists
$this->template_dir = $template_dir;
}
}
public function render($template_file) {
if (file_exists($this->template_dir.$template_file)) {
include $this->template_dir.$template_file;
} else {
throw new Exception('no template file ' . $template_file . ' present in directory ' . $this->template_dir);
}
}
public function __set($name, $value) {
$this->vars[$name] = $value;
}
public function __get($name) {
return $this->vars[$name];
}
} ?>
hmm,I have no idea How I can detect the _POST params
if(isset($_POST['Post']))
{
$model->attributes=$_POST['Post'];
if($model->save())
$this->redirect(array('view','id'=>$model->id));
}
this is the Yii framework I observed. How could I detect params whether is $_POST or $_GET after load a specific views.
Any guidance and advice to archive my tasks?
Unrelared to question You have one major problem: your ability to express what mean is extremely limited. The question, which you asked, was actually unrelated to your problem.
From what I gather, you need to detect of user made a POST or GET request. Do detect it directly you can check $_SERVER['REQUEST_METHOD'], but checking it withing controller might be quite bothersome. You will end up with a lot of controller's methods which behave differently based on request method.
Since you are not using any of popular frameworks, is would recommend for you to instead delegate this decision to the routing mechanism.
A pretty good way to handle this, in my opinion, is to prefix the controller's method names with the request method: postLogin(), getArticles() etc. You can find few additional example here. If there is a POST request, it will have something in $_POST array.
What are calling "views" are actually templates. If you read this article, you will notice, that the code there is actually an improved version of your MyView. Views are not templates. Views are instances which contain presentation logic and manipulate multiple templates.
P.S. If you are exploring MVC and MVC-inspired patterns in relation to PHP, you might find this post useful.
I have a larger application with a Frontcontroller in php that handles incoming ajax requests. I am thinking about a good way to handle Action->Method mapping, this controller is in charge of instantiating other classes and executing methods there.
The switch is just getting too big and it's ugly. I was thinking about creating an array and simply doing:
if(in_array($action, $methodmap)){
$methodmap[$action]();
}
But not sure of how efficient that would be or if there are any other better alternatives, performance is important since this controller handles a whole lot of incoming requests.
Thanks!
You could create a simple routing system.
index.php
<?php
class InvalidClassException extends Exception {}
function autoloader($class)
{
$path = 'controllers/'.$class.'.php';
if (!ctype_alnum($class) || !file_exists($path))
throw new InvalidClassException("Couldn't find '$class'");
require($path);
}
spl_autoload_register('autoloader');
$request = isset($_GET['request'])?$_GET['request']:'front';
$controller = new $request();
$controller->index();
And a directory controllers/ where you store all your controllers. E.g.
controllers/test1.php
<?php
class Test1
{
public function index()
{
print "Test 1";
}
}
When accessing index.php?request=test1, Test1->index() would be called, thus output
Test 1
TRy using a "routing" configuration file instead... that way, you can add new routings to the application without needing to change the actual action/method mapping code
In my bootstrap.php I have many _initX() functions, and some of them may contain code that depends on code in the previous initX
protected function _initAutoloading() { }
protected function _initViewInitializer() { }
protected function _initVariables() { }
So my question, are these _init functions guaranteed to be executed in the exact order they've been declared?
EDIT - To provide a more direct answer to your question, I would say that they probably will be since the code uses ReflectionObjects::getmethods() or get_class_methods depending on your PHP version, so I believe those will return the function in order but there is nothing in the PHP docs or Zend docs that guarantee this will always be the case, so I would not consider this a supported feature.
You can pass the names of the resource functions you want/need to call as part of the bootstrap call: $bootstrap->bootstrap(array('foo', 'bar')); instead of not passing anything and let the Zend Application call them all automatically in which you are not sure of the order.
If you have dependencies in between your bootstrap resources however, I suggest you look at Resource plugins which will allow you to separate your code in different classes and easily call $bootstrap('foo') from within your 'bar' resource plugin code (though you can do so with the _init*() functions as well)
Another benefit of resource plugins is they can be shared with other bootstrap files if you need to and they are easier to test than _init*() functions.
Make sure you read theory of operation document from the Zend Application doc
If you really need them invoked in a particular order, you should use a helper list:
var $init_us = array(
"_initAutoloading",
"_initViewInitializer",
"_initVariables",
);
function __construct() {
foreach ($this->init_us as $fn) {
$this->{$fn}();
}
}
To use that construct in ZF you could rename the example __construct into _initOrderedList and your custom _initFunctions into _myinit... or something.
Read the manual. There are a section called Dependency Tracking :
If a resource depends on another resource, it should call bootstrap() within its code to ensure that resource has been executed. Subsequent calls to it will then be ignored.
Here is sample code :
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
protected function _initRequest()
{
// Ensure the front controller is initialized
$this->bootstrap('FrontController');
// Retrieve the front controller from the bootstrap registry
$front = $this->getResource('FrontController');
$request = new Zend_Controller_Request_Http();
$request->setBaseUrl('/foo');
$front->setRequest($request);
// Ensure the request is stored in the bootstrap registry
return $request;
}
}
You don't have to rely on the order.