Third_party Views Path(s) Check in CodeIgniter 3 - php

I'm trying to check if a view file really exists according to the third_party paths loaded
Normally, I'd check if a view exists with is_file(APPPATH.'/views/folder/'.$view)
I can retrieve every loaded third_party paths with get_package_paths (thanks to the comment of Tpojka) and then check in their folder views if the file exists,
but I was hoping for a 'direct' check, as if the ->view function would return false instead of redirecting to an error page
$html = $this->load->view($tpl,'',TRUE) ? $this->load->view($tpl,'',TRUE) : $another_template;
Though I realize there might be no other solutions that adding this manual check with a loop through the loaded paths and hide it in a CI_Load Class extension (application/core/MY_Loader) to give the apparance of a direct check in the controller:
EDIT: This is a bad idea, cause view() may return false to CI function that might not be designed for
class MY_Loader extends CI_Lodaer{
public function __construct() {
parent::__construct();
}
public function view($view, $vars = array(), $return = FALSE)
{
foreach( $this->get_package_paths( TRUE ) as $path )
{
// this will only retrieve html from the first file found
if( is_file( $path."/views/".$view ) ) return parent::view($view, $vars, $return);
}
// if no match
return false;
}
}
What I find annoying is that load->view already makes a check through the paths, so this solution will add a second check and increase server consumption..

Well in the end I choose this lukewarm solution :
Instead of extending the function view() for it to return false (and having to deal with it through CI then after !) , I just made a function is_view() in the application/core/MY_Loader.php
I'm not sure MY_Loader is the correct place to place such a function, but so far it did the trick for me ...
(thx Tpojka for the indication)
in application/core/MY_Loader.php
/**
* is_view
*
* Check if a view exists or not through the loaded paths
*
* #param string $view The relative path of the file
*
* #return string|bool string containing the path if file exists
* false if file is not found
*/
public function is_view($view)
{
// ! BEWARE $path contains a beginning trailing slash !
foreach( $this->get_package_paths( TRUE ) as $path )
{
// set path, check if extension 'php'
// (would be better using the constant/var defined for file extension of course)
$path_file = ( strpos($view,'.php') === false ) ? $path."views/".$view.'.php' : $path."views/".$view ;
// this will return the path at first match found
if( is_file( $path_file ) ) return $path."views/";
}
// if no match
return false;
}
and in application/controllers/Welcome.php
$view = "frames/my_html.php";
/*
* the view file should be in
* application/third_party/myapp/views/frames/my_html.php
*
* so far, if the file does not exists, and we try
* $this->load->view($view) will redirect to an error page
*/
// check if view exists and retrieve path
if($possible_path = $this->load->is_view($view))
{
//set the data array
$data = array("view_path"=>$possible_path);
// load the view knowing it exists
$this->load->view($view,$data)
}
else echo "No Template for this frame in any Paths !";
and of course in the view
<h1>My Frame</h1>
<p>
The path of this file is <=?$view_path?>
</p>

Related

Why Codeignitor does not accept autoload Controller classes when validating routes?

Why does Codeignitor not accept Controller in composer autoload when validating routes?
It's checking by: class_exists($class, FALSE) where the second parameter disables checking in autoload.
https://github.com/bcit-ci/CodeIgniter
$e404 = FALSE;
$class = ucfirst($RTR->class);
$method = $RTR->method;
if (empty($class) OR ! file_exists(APPPATH.'controllers/'.$RTR->directory.$class.'.php'))
{
$e404 = TRUE;
}
else
{
require_once(APPPATH.'controllers/'.$RTR->directory.$class.'.php');
if ( ! class_exists($class, FALSE) OR $method[0] === '_' OR method_exists('CI_Controller', $method))
{
$e404 = TRUE;
}
elseif (method_exists($class, '_remap'))
{
$params = array($method, array_slice($URI->rsegments, 2));
$method = '_remap';
}
elseif ( ! method_exists($class, $method))
{
$e404 = TRUE;
}
/**
* DO NOT CHANGE THIS, NOTHING ELSE WORKS!
*
* - method_exists() returns true for non-public methods, which passes the previous elseif
* - is_callable() returns false for PHP 4-style constructors, even if there's a __construct()
* - method_exists($class, '__construct') won't work because CI_Controller::__construct() is inherited
* - People will only complain if this doesn't work, even though it is documented that it shouldn't.
*
* ReflectionMethod::isConstructor() is the ONLY reliable check,
* knowing which method will be executed as a constructor.
*/
elseif ( ! is_callable(array($class, $method)))
{
$reflection = new ReflectionMethod($class, $method);
if ( ! $reflection->isPublic() OR $reflection->isConstructor())
{
$e404 = TRUE;
}
}
}
Looking over the git history, the change was introduced in 49e68de96b420a444c826995746a5f09470e76d9, with the commit message being:
Disable autoloader call from class_exists() occurences to improve performance
Note: The Driver libary tests seem to depend on that, so one occurence in CI_Loader is left until we resolve that.
So the nominal reason is performance.
If you want to ensure that the controller classes will be loaded on each request, you can add the files explicitly to the Composer autoload.files attribute, like so:
composer.json
{
"autoload": {
"files": [
"src/Foo.php"
]
},
"name": "test/64166739"
}
src/Foo.php
<?php
class Foo {}
test.php
<?php
$loader = require('./vendor/autoload.php');
var_dump(class_exists('Foo', false));
When run (via php test.php for example), we get the following output:
bool(true)
Additional
Looking over the code around that call to class_exists, it would appear that the controller files should follow a convention such that, for example with the built in Welcome controller and the default settings, the file that defines it should exist at:
application/controllers/Welcome.php
and so after require_onceing that file, the call to class_exists is a reasonably simple sanity check to ensure that the file did in fact define that class. So, based on this assumption about how controllers are added to the CodeIgniter application (ie all in the application/controllers directory and named the same as the class that they define), it's reasonable to bypass the autoloader when performing that check.
If you wanted to ensure the controllers are loaded when needed, the CodeIgniter way, they should be added to the application as listed above.

Getting parts of a URL in PHP

How can I extract the following parts using PHP function:
The Domain
The path without the file
The file
The file with Extension
The file without Extension
The scheme
The port
The query
The fragment
(add any other that you think would be useful)
Ex.1
https://stackoverflow.com/users/test/login.php?q=san&u=post#top
The Domain (stackoverflow.com)
The path without the file (/users/test/)
The file(login.php)
The file Extension (.php)
The file without Extension (login)
The scheme(https:)
The port(return empty string)
The query(q=san&u=post)
The fragment(top)
Ex: 2 stackoverflow.com/users/test/login.php?q=san&u=post#top
The Domain (stackoverflow.com)
The path without the file (/users/test/)
The file(login.php)
The file Extension (.php)
The file without Extension (login)
The scheme(return empty string)
The port(return empty string)
The query(q=san&u=post)
The fragment(top)
Ex: 3 /users/test/login.php?q=san&u=post#top
The path without the file (/users/test/)
The file(login.php)
The file Extension (.php)
The file without Extension (login)
The query(q=san&u=post)
The fragment(top)
For remaining (return empty string)
Ex: 4 /users/test/login?q=san&u=post#top
The path without the file (/users/test/)
The file(login)
The file Extension (return empty string)
The file without Extension (login)
The query(q=san&u=post)
The fragment(top)
For remaining (return empty string)
Ex: 5 login?q=san&u=post#top
The file(login)
The file Extension (return empty string)
The file without Extension (login)
The query(q=san&u=post)
The fragment(top)
For remaining (return empty string)
Ex: 6 ?q=san&u=post
The query(q=san&u=post)
For remaining (return empty string)
I checked parse_url function, but doesn't return what I need. Since, I'm beginner in PHP, it was difficult for me. If you have any idea, please answer.
Thanks in advance.
PHP provides a parse_url function.
This function parses a URL and returns an associative array containing
any of the various components of the URL that are present.
This function is not meant to validate the given URL, it only breaks
it up into the above listed parts. Partial URLs are also accepted,
parse_url() tries its best to parse them correctly.
You can see the test cases executed here.
$urls = array(
"https://stackoverflow.com/users/test/login.php?q=san&u=post#top",
"/users/test/login.php?q=san&u=post#top",
"?q=san&u=post#top",
"login.php?q=san&u=post#top",
"/users/test/login?q=san&u=post#top",
"login?q=san&u=post#top"
);
foreach( $urls as $x ) {
echo $x . "\n";
var_dump( parse_url($x) );
}
I"m using this to locate the root and webroot
<?php
/**
* #brief get paths from the location where it was executed.
*/
class PathHelper {
/**
* #brief This function tries to determine the FileSystem root of the application. (needs to be executed in the root)
* #return string
*/
public static function locateRoot($file) {
$dir = dirname($file);
/** FIX Required for WINDOWS * */
$dir = preg_replace('/\\\\/', '/', $dir);
$dir = preg_replace('/\\\/', '/', $dir);
return $dir;
}
/**
* #brief This function tries to determine the WebRoot. (needs to be executed in the root)
* #return string
*/
public static function locateWebroot($file) {
$docroot = $_SERVER['DOCUMENT_ROOT'];
$dir = dirname($file);
if ($dir == $docroot) {
$webroot = "";
} else {
$webroot = substr_replace($dir, '', 0, strlen($docroot));
}
/** FIX Required for WINDOWS * */
$webroot = preg_replace('/\\\\/', '/', $webroot);
$webroot = preg_replace('/\\\/', '/', $webroot);
return $webroot;
}
}
I set this as a constant so i can use it throughout my application.
For example:
For a menu you can do something like this:
// the requested url
$requestedUrl = $_SERVER['REQUEST_URI'];
// remove the webroot from the requested url
$requestedUrl = str_replace(WEBROOT, "", $_SERVER['REQUEST_URI']);
// take away the / if they still exist at the beginning
$requestedUrl = ltrim($requestedUrl, "/");
Then i got this:
index.php?controller=User&action=overview
This equals to my url of one of my menu items.
You could use explode on this last url to find all the other values you want.
Edit: Its probably better to use parse_url(). I am not used to all the functions in PHP but if nothing works then this is atleast a fallback.

Search through a directory in PHP, beginning at the end [duplicate]

This question already has answers here:
How to use return inside a recursive function in PHP
(4 answers)
Closed 9 months ago.
I have this PHP code, but I have only written one recursive function before. I want to begin in a particular subdirectory and check for the presence of a file. If it doesn't exist in the directory I want to check the parent folder for the file and then recursively go on until I either don't find the file and return "" or find the file and return its contents. Here is my function:
/**
* Recursive function to get sidebar from path and look up tree until we either
* find it or return blank.
*/
class MyClass{
public function findSidebarFromPath($path){
if(file_exists($_SERVER["DOCUMENT_ROOT"] . implode("/", $path) . "/sidebar.html")){
return file_get_contents($_SERVER["DOCUMENT_ROOT"] . implode("/", $path) . "/sidebar.html");
} else {
if(count($path) > 0){
# Pop element off end of array.
$discard = array_pop($path);
$this->findSidebarFromPath($path);
} else {
return "";
}
}
}
}
So when I call it, I do this:
$myclass = new MyClass();
$myclass->findSidebarFromPath(array("segment1", "segment2", "segment3"));
Let's say the file I am trying to find is in a directory called "segment2". The function never finds it. The array_pop function doesn't pop off the element off the end of the array and then call the findSidebarFromPath function.
If you write this as a standalone function, it will probably be more useful to you in other areas. After we understand how it works, we'll show you how you can add a public function to your class that can utilize it.
/**
* #param $root string - the shallowest path to search in
* #param $path string - the deepest path to search; this gets appended to $root
* #param $filename string - the file to search for
* #return mixed - string file contents or false if no file is found
*/
function findFile($root, $path, $filename) {
// create auxiliary function that takes args in format we want
$findFileAux = function($cwd, $filename) use (&$findFileAux, $root) {
// resolve the complete filename
$file = "{$cwd}/{$filename}";
// if the file exists, return the contents
if (file_exists($file)) return file_get_contents($file);
// if the cwd has already reached the root, do not go up a directory; return false instead
if ($cwd === $root) return false;
// otherwise check the above directory
return $findFileAux(dirname($cwd), $filename);
};
// starting checking for the file at the deepest segment
return $findFileAux("{$root}/{$path}", $filename);
}
Check the example output on ideone.
So here's how to use it
findFile($_SERVER["DOCUMENT_ROOT"], "foo/bar/qux", "sidebar.html");
Here's how you would integrate it with your class. Notice that this public function has the same API as in your original code
class MyClass {
/**
* #param $path array - path segments to search in
* #return mixed - string file contents or false if sidebar is not found
*/
public function findSidebarFromPath($path) {
// Here we call upon the function we wrote above
// making sure to `join` the path segments into a string
return findFile($_SERVER["DOCUMENT_ROOT"], join("/", $path), "sidebar.html";
}
}
Additional explanation
If $_SERVER["DOCUMENT_ROOT"] is /var/www...
Check /var/www/foo/bar/qux/sidebar.html, return if exists
Check /var/www/foo/bar/sidebar.html, return if exists
Check /var/www/foo/sidebar.html, return if exists
Check /var/www/sidebar.html, return if exists
Because we got to the root (/var/www) no further searches will happen
return false if sidebar.html did not exist in any of the above
Here's the same function with the explanatory comments removed
/**
* #param $root string - the shallowest path to search in
* #param $path string - the deepest path to search; this gets appended to $root
* #param $filename string - the file to search for
* #return mixed - string file contents or false if no file is found
*/
function findFile($root, $path, $filename) {
$findFileAux = function($cwd, $filename) use (&$findFileAux, $root) {
$file = "{$cwd}/{$filename}";
if (file_exists($file)) return file_get_contents($file);
if ($cwd === $root) return false;
return $findFileAux(dirname($cwd), $filename);
};
return $findFileAux("{$root}/{$path}", $filename);
}
You might also want to consider using DIRECTORY_SEPARATOR instead of the hard-coded "/" so that this code could be used reliably on a variety of platforms.

Loading Codeigniter library from a different folder under application folder

Hi I am having an issue
Say I have a folder structure in CodeIgniter
application/
controllers/
models/
views/
gmail_library/
Now I have written a controller
class invite_friends extends CI_Controller {
function __construct() {
parent::__construct();
$this->load->gmail_library('Config'); // this line is giving me error
session_start();
}
}
How can I set this thing like this?
First of all, note that CodeIgniter doesn't use overloading by __call() to implement dynamic methods. Thus there is no way to get such a gmail_library() methods to work.
The conventional method
From the User Guide:
Your library classes should be placed within your
application/libraries folder, as this is where CodeIgniter will look
for them when they are initialized.
If you're using CI Loader class to load a library or helper, you should follow CI's conventions.
application/libraries/Myclass.php
$this->load->library('myclass');
$this->myclass->my_method();
Using relative paths
1) You put your library files in sub-directories within the main libraries folder:
application/libraries/gmail/Gmail_config.php
I renamed your Config.php file name to prevent the occurrence of conflict with CI config core class.
$this->load->library('gmail/gmail_config');
2) Also you can use relative path within the Loader::library() method to load the library file from the outside of the library folder, as follows:
The path to the file is relative. So you can use ../ to go one UP level in path.
Again: I renamed your Config.php file name to prevent the occurrence of conflict with CI config core class.
$this->load->library('../gmail_library/Gmail_config');
An old question, I know, but I came across this looking for a way to use classes (libraries) from outside the application folder and I liked to keep it in 'the CI way of doing this'. I ended up extending the CI_Loaderclass:
I basically copied the _ci_load_class function and added an absolute path
<? if (!defined('BASEPATH')) exit('No direct script access allowed');
class MY_Loader extends CI_Loader {
protected $absPath = '/home/xxxxx/[any-path-you-like]/common/';
/**
* Load class
*
* This function loads the requested class.
*
* #param string the item that is being loaded
* #param mixed any additional parameters
* #param string an optional object name
* #return void
*/
public function commonLibrary($class, $params = NULL, $object_name = NULL)
{
// Get the class name, and while we're at it trim any slashes.
// The directory path can be included as part of the class name,
// but we don't want a leading slash
$class = str_replace('.php', '', trim($class, '/'));
// Was the path included with the class name?
// We look for a slash to determine this
$subdir = '';
if (($last_slash = strrpos($class, '/')) !== FALSE)
{
// Extract the path
$subdir = substr($class, 0, $last_slash + 1);
// Get the filename from the path
$class = substr($class, $last_slash + 1);
}
// We'll test for both lowercase and capitalized versions of the file name
foreach (array(ucfirst($class), strtolower($class)) as $class)
{
$subclass = $this->absPath.'libraries/'.$subdir.config_item('subclass_prefix').$class.'.php';
// Is this a class extension request?
if (file_exists($subclass))
{
$baseclass = BASEPATH.'libraries/'.ucfirst($class).'.php';
if ( ! file_exists($baseclass))
{
log_message('error', "Unable to load the requested class: ".$class);
show_error("Unable to load the requested class: ".$class);
}
// Safety: Was the class already loaded by a previous call?
if (in_array($subclass, $this->_ci_loaded_files))
{
// Before we deem this to be a duplicate request, let's see
// if a custom object name is being supplied. If so, we'll
// return a new instance of the object
if ( ! is_null($object_name))
{
$CI =& get_instance();
if ( ! isset($CI->$object_name))
{
return $this->_ci_init_class($class, config_item('subclass_prefix'), $params, $object_name);
}
}
$is_duplicate = TRUE;
log_message('debug', $class." class already loaded. Second attempt ignored.");
return;
}
include_once($baseclass);
include_once($subclass);
$this->_ci_loaded_files[] = $subclass;
return $this->_ci_init_class($class, config_item('subclass_prefix'), $params, $object_name);
}
// Lets search for the requested library file and load it.
$is_duplicate = FALSE;
foreach ($this->_ci_library_paths as $path)
{
$filepath = $this->absPath.'libraries/'.$subdir.$class.'.php';
// Does the file exist? No? Bummer...
if ( ! file_exists($filepath))
{
continue;
}
// Safety: Was the class already loaded by a previous call?
if (in_array($filepath, $this->_ci_loaded_files))
{
// Before we deem this to be a duplicate request, let's see
// if a custom object name is being supplied. If so, we'll
// return a new instance of the object
if ( ! is_null($object_name))
{
$CI =& get_instance();
if ( ! isset($CI->$object_name))
{
return $this->_ci_init_class($class, '', $params, $object_name);
}
}
$is_duplicate = TRUE;
log_message('debug', $class." class already loaded. Second attempt ignored.");
return;
}
include_once($filepath);
$this->_ci_loaded_files[] = $filepath;
return $this->_ci_init_class($class, '', $params, $object_name);
}
} // END FOREACH
// One last attempt. Maybe the library is in a subdirectory, but it wasn't specified?
if ($subdir == '')
{
$path = strtolower($class).'/'.$class;
return $this->_ci_load_class($path, $params);
}
// If we got this far we were unable to find the requested class.
// We do not issue errors if the load call failed due to a duplicate request
if ($is_duplicate == FALSE)
{
log_message('error', "Unable to load the requested class: ".$class);
show_error("Unable to load the requested class: ".$class);
}
}
}
Put the file MY_Loader.php in the application/core folder and load your libs with:
$this->load->commonLibrary('optional_subfolders/classname', 'classname');
$this->classname->awesome_method();

How to remove a previously added script file in a Zend Framework controller?

Is it possible to remove a script that was previously added like this?
$this->view->headScript()->appendFile("/js/my.js");
The situation here is that in my Bootstrap.php several JavaScript files are appended like in the above example but in a particular controller I want one particular JavaScript file not to be loaded. Is there a way to remove it during the initiation of the controller?
I am looking for something like this.
$this->view->headScript()->removeFile("/js/my.js");
This works, but really isn't a good practice but rather for exceptional purposes. I recommend trying to not load the unwanted scripts in the first place.
It is possible to remove scripts subsequently with a function like this one.
/**
* Removes a previously appended script file.
*
* #param string $src The source path of the script file.
* #return boolean Returns TRUE, if the removal has been a success.
*/
public function removeScript($src) {
$headScriptContainer = Zend_View_Helper_Placeholder_Registry::getRegistry()
->getContainer("Zend_View_Helper_HeadScript");
$iter = $headScriptContainer->getIterator();
$success = FALSE;
foreach ($iter as $k => $value) {
if(strpos($value->attributes["src"], $src) !== FALSE) {
$iter->offsetUnset($k);
$success = TRUE;
}
}
Zend_View_Helper_Placeholder_Registry::getRegistry()
->setContainer("Zend_View_Helper_HeadScript",$headScriptContainer);
return $success;
}
Note that the strpos function is used here. This will remove every script that has $src in its path. Of course you can change that to your needs.
It's an old question, but here is an alternative:
I used the minifyHeadScript and changed his helper adding this custom function:
/**
* Removes all script file.
*
*/
public function clearAllScripts(){
$this->getContainer()->exchangeArray(array());
$container = $this->getContainer();
Zend_View_Helper_Placeholder_Registry::getRegistry()->setContainer("Zend_View_Helper_HeadScript", $container);
}

Categories