I have hard time understanding PHP namespace. So, this post is things I learn from working with it.
Namespace keyword starts the file.
When you include the class that has namespace defined, you also need to type the keyword use and the namespace you want to use follows. Without using namespace, this would work. However, with namespace, the keyword use is required.
The main purpose of namspaces in PHP is to avoid name conflict.
Namespace is used in class. You cannot declare a namespace and in that file don’t declare a class.
In one file, you can have class definition and code (functions, variables) outside of that class. This is strange for me since I learn OOP first from Java. You cannot access such functions/properties from other files though (even if you include it). However, these functions, variables are accessible in the classes of the same file. Maybe it’s a way of code sharing without declaring a class with static functions/properties.
spl_autoload_register() without arguments will invoke (call) another function called spl_autoload when php script tries to access an unknown class. The name of the unknown class will be passed to spl_autoload. spl_autoload first then find if there is a file name identical to the class name (lower and upper case, php 7) with the extension .php or .inc in: First, the include path then the local directory. The search will stop once the file is found (not when the class is found). Sounds confusing? Let me demonstrate:
Let’s make it clearer:
So, I have the root folder with two files: index.php and cat.php (lowercase C). One folder called animal that contains Cat.php (uppercase)
Here are the code in each file:
cat.php
<?php
class Cat
{
function __construct($name)
{
echo "I am a cat in the local directory";
}
}
Cat.php (inside animal folder)
<?php
class Cat
{
function __construct($name)
{
echo "I am the cat in the ANIMAL directory";
}
}
I set the animal to the include path. PHP will search for file names with extensions .php or .inc in those paths first. If the file name is found (in this case is Cat.php/Cat.inc/cat.php/cat.inc), then the search will stop. Remember that the search doesn’t care if the class exists. It only cares if the file exists. If I comment out the implementation of the Cat class in animal/Cat.php and access the index.php again, I’ll get an error:
Fatal error: Uncaught Error: Class ‘Cat’ not found in C:\xampp\htdocs\lab\oop\index.php:6 Stack trace: #0 {main} thrown in C:\xampp\htdocs\lab\oop\index.php on line 6
Now, if I comment out the set_include_path line in index.php, I’ll get the Cat class in the root directory:
Trait is something a class has. A trait doesn’t define a type of a class. For example, Teacher is a type. Talk a lot is a trait. A teacher can either talk a lot or not.
trait TalkALot
{
public function talk()
{
echo "I talk a lot";
}
}
class Teacher
{
private $name;
public function __construct($name)
{
$this->name = $name;
}
use TalkALot;
}
$teacher = new Teacher("Oma");
$teacher->talk();
The code above will output “I talk a lot”
One class can implement multiple traits. However, those traits must not have same function name.
trait TalkALot
{
public function talk()
{
echo "I talk a lot";
}
}
trait TalkALittle
{
public funtion talk(){
echo "I don't talk";
}
}
class Teacher
{
private $name;
public function __construct($name)
{
$this->name = $name;
}
use TalkALot, TalkALittle;
}
$teacher = new Teacher("Oma");
$teacher->talk();
The code above will cause an error:
Fatal error: Trait method talk has not been applied, because there are collisions with other trait methods on Teacher in C:\xampp\htdocs\lab\oop\index.php on line 21
You can use the keyword insteadof as a workaround in case your traits have same method name:
trait TalkALot
{
public function talk()
{
echo $this->name . " talk all days";
}
}
trait TalkALittle
{
public function talk()
{
echo $this->name . " talk a little";
}
}
class Teacher
{
private $name;
use TalkALot, TalkALittle {
TalkALittle::talk insteadof TalkALot;
}
public function __construct($name)
{
$this->name = $name;
}
}
$teacher = new Teacher("Oma");
$teacher->talk();
What if you want to use the other method that get overridden?
trait TalkALot
{
public function talk()
{
echo $this->name . " talk all days";
}
}
trait TalkALittle
{
public function talk()
{
echo $this->name . " talk a little";
}
}
class Teacher
{
private $name;
use TalkALot, TalkALittle {
TalkALot::talk insteadof TalkALittle;
TalkALittle::talk as modestTalk;
}
public function __construct($name)
{
$this->name = $name;
}
}
$teacher = new Teacher("Oma");
$teacher->modestTalk();
Here, I’ve defined an alias for the talk method of TalkALittle trait. Then, I can call the talk method in TalkALittle trait as modestTalk.
Trait can have private function, just like class.
Since trait is something a class has, it has access to all class’s variable and methods
trait TalkALot
{
public function talk()
{
$this->requestSayName();
}
}
class Teacher
{
private $name;
public function __construct($name)
{
$this->name = $name;
}
public function requestSayName()
{
echo "Say my name!";
}
use TalkALot;
}
$teacher = new Teacher("Oma");
$teacher->talk();
As you can see, the function talk is in the trait TalkALot. The $teacher instance call it. The talk function call the function requestSayname, which is defined in the Teacher class.
You cannot define a property in a trait then define a property with the same name in the class
The following code generates an error:
trait TalkALot
{
private $name = "Trait";
public function talk()
{
$this->requestSayName();
}
public function sayName()
{
echo $this->name;
}
}
class Teacher
{
private $name;
public function __construct($name)
{
$this->name = $name;
}
public function requestSayName()
{
echo "Say my name!";
}
use TalkALot;
}
$teacher = new Teacher("Oma");
$teacher->sayName();
Fatal error: Teacher and TalkALot define the same property ($name) in the composition of Teacher. However, the definition differs and is considered incompatible. Class was composed in C:\xampp\htdocs\lab\oop\index.php on line 20
Trait can have static method. Static method in trait can access via the classes that use it either by class name or an instance:
trait TalkALot
{
public function talk()
{
echo $this->name . " talk all days";
}
public static function tryTalking()
{
echo "bla bla <br />";
}
}
trait TalkALittle
{
public function talk()
{
echo $this->name . " talk a little";
}
}
class Teacher
{
private $name;
use TalkALot, TalkALittle {
TalkALot::talk insteadof TalkALittle;
TalkALittle::talk as modestTalk;
}
public function __construct($name)
{
$this->name = $name;
}
}
$teacher = new Teacher("Oma");
Teacher::tryTalking();
$teacher::tryTalking();
Filters are the source of confusion when I first started my WP development. I only gained a better understanding of this topic until recently. Thus, I want to share with you my finding.
As you may already know (I hope), WordPress users can modify parts of WordPress website with filter. You use the add_filter function to add your own function to modify a particular item. For example, you want to add a few words before the title of every page/post of your blog, you can do like this:
add_action('the_title', 'modify_the_title');
‘modify_the_title’ is the function you need to define to do the modifications.
Think of functions like ‘modify_the_title’ as a blackbox
The $wp_filter variable
To understand how add_filter works, you need to know a global variable called $wp_filter. It’s an array. Let’s see how is it structured.
Since it’s a global variable, we can access it anywhere. Let’s verify it’s actually an array by entering this code in our current active theme’s functions.php
echo '<h1>$wp_fitler type</h1>';
echo gettype($wp_filter);
echo '<h1>Number of elements in $wp_fitler</h1>';
echo count($wp_filter);
echo '<h1>List of hooks in $wp_filter</h1>';
foreach ($wp_filter as $key => $value)
{
echo "<p>$key</p>";
}
die();
Open the website, we’ll see this:
As you can see, it’s an array with 658 elements. With the foreach loop, you can also see that it’s an associative array with the key is the name of the hooks.
So, I want to see the details of ‘the_title’ hook. Let’s replace the above code with this one:
I also installed var_dumper, a very nice library to pretty print the var_dump to the screen. Here is what we have:
As you can see, $wp_filter is an associative array where the keys are the name of the hook and the values are WP_Hook objects. The most important part here is the callbacks field. It’s an array contains the details of functions that registered to the hook.
In case you are curious, callbacks is an associative array (not indexed). What the number 10, 11 means is the order of execution.
How custom functions are added to $wp_filter
If you read the documentation of add_filter or add_action, the third argument is the priority or execution. The larger the number, the later it will be executed.
So, if I add the following function to the_title hook (notice that I set the priority to 1):
add_filter('the_title', 'my_custom_function', 1, 0);
function my_custom_function($title)
{
return $title;
}
And we check the dump of the_title hook object again:
You can see that callbacks now has another element with key is ‘1’ that store the newly added function.
Adding functions to your own filters
I usually wonder, how filters hooked get registered at the first place. Do we have a function called ‘register_filter’?
No, there isn’t a function like that. When you call the add_filter function, if the filter name doesn’t exists in $wp_filter, it will be added to the $wp_filter array. Let me demonstrate.
add_filter('filter_hook_that_does_not_exist', 'my_custom_function', 1, 0);
function my_custom_function($input)
{
/** code to modify the input here */
return $input;
}
echo '<h1>hook details</h1>';
echo '<h3>type</h3>';
echo gettype($wp_filter['filter_hook_that_does_not_exist']);
echo '<h3>details</h3>';
dump($wp_filter['filter_hook_that_does_not_exist']);
die();
As you can see, we add a filter to a non-existence hook. Let’s see the result:
So, how the function ‘my_custom_function’ get executed? Well, that’ the topic of the post about apply_filters.
Conclusion
Hopefully now you are like me, understand how are custom functions get added to the $wp_filter every time we call add_filter function. If you have questions, please let me know
This post is another effort of mine in understanding the core of WordPress. Hopefully at the end of this post, you’ll gain something about. My goal is to have better understanding of the way WP render particular parts of the website by using the filters.
Dive into the header.php file of WP theme
I’m using store front theme on my local dev site. However, you’ll encounter the same layout/code in any other themes.
This is the part of the code that renders the header
As you can see, there is no title tag. However, there is a piece of PHP code that generates the title and other stuffs. That’s wp_head()
Dive into wp_head function
The code for wp_head is surprisingly simple:
function wp_head() {
/**
* Prints scripts or data in the head tag on the front end.
*
* @since 1.5.0
*/
do_action( 'wp_head' );
}
It calls another function called do_action with the parameter ‘wp_head’. I hope you have, at least a vague understanding of how filters and actions work in WordPress.
Now, we need to know where the title’s filters are added to the wp_head hook. With the help of PhpStorm, I was able to locate this part:
Once again, the function is very short since it calls other functions. Our next step would be investigating the function ‘wp_get_document_title’
function wp_get_document_title() {
/**
* Filters the document title before it is generated.
*
* Passing a non-empty value will short-circuit wp_get_document_title(),
* returning that value instead.
*
* @since 4.4.0
*
* @param string $title The document title. Default empty string.
*/
$title = apply_filters( 'pre_get_document_title', '' );
if ( ! empty( $title ) ) {
return $title;
}
global $page, $paged;
$title = array(
'title' => '',
);
// If it's a 404 page, use a "Page not found" title.
if ( is_404() ) {
$title['title'] = __( 'Page not found' );
// If it's a search, use a dynamic search results title.
} elseif ( is_search() ) {
/* translators: %s: search phrase */
$title['title'] = sprintf( __( 'Search Results for “%s”' ), get_search_query() );
// If on the front page, use the site title.
} elseif ( is_front_page() ) {
$title['title'] = get_bloginfo( 'name', 'display' );
// If on a post type archive, use the post type archive title.
} elseif ( is_post_type_archive() ) {
$title['title'] = post_type_archive_title( '', false );
// If on a taxonomy archive, use the term title.
} elseif ( is_tax() ) {
$title['title'] = single_term_title( '', false );
/*
* If we're on the blog page that is not the homepage or
* a single post of any post type, use the post title.
*/
} elseif ( is_home() || is_singular() ) {
$title['title'] = single_post_title( '', false );
// If on a category or tag archive, use the term title.
} elseif ( is_category() || is_tag() ) {
$title['title'] = single_term_title( '', false );
// If on an author archive, use the author's display name.
} elseif ( is_author() && $author = get_queried_object() ) {
$title['title'] = $author->display_name;
// If it's a date archive, use the date as the title.
} elseif ( is_year() ) {
$title['title'] = get_the_date( _x( 'Y', 'yearly archives date format' ) );
} elseif ( is_month() ) {
$title['title'] = get_the_date( _x( 'F Y', 'monthly archives date format' ) );
} elseif ( is_day() ) {
$title['title'] = get_the_date();
}
// Add a page number if necessary.
if ( ( $paged >= 2 || $page >= 2 ) && ! is_404() ) {
$title['page'] = sprintf( __( 'Page %s' ), max( $paged, $page ) );
}
// Append the description or site title to give context.
if ( is_front_page() ) {
$title['tagline'] = get_bloginfo( 'description', 'display' );
} else {
$title['site'] = get_bloginfo( 'name', 'display' );
}
/**
* Filters the separator for the document title.
*
* @since 4.4.0
*
* @param string $sep Document title separator. Default '-'.
*/
$sep = apply_filters( 'document_title_separator', '-' );
/**
* Filters the parts of the document title.
*
* @since 4.4.0
*
* @param array $title {
* The document title parts.
*
* @type string $title Title of the viewed page.
* @type string $page Optional. Page number if paginated.
* @type string $tagline Optional. Site description when on home page.
* @type string $site Optional. Site title when not on home page.
* }
*/
$title = apply_filters( 'document_title_parts', $title );
$title = implode( " $sep ", array_filter( $title ) );
$title = wptexturize( $title );
$title = convert_chars( $title );
$title = esc_html( $title );
$title = capital_P_dangit( $title );
return $title;
}
This function is quite lengthy since it does all of jobs of generating the title. You can also see that, while the title tag is a small part of the document, this function isn’t a simple one at all. It’s partly because WordPress offers so much flexibility for developers to modify the titles and other parts of the content.
As you read through the code, you’ll see that this function checks for what kind of page it’s currently on (whether it’s a homepage/archive page…) and render the title for such pages accordingly.
You may have some confusions regarding the apply_filters part. That’s the topic of another post which I’m going to write shortly.
OsTicket is an awesome ticketing system. It’s better than Hesk in my opinion. However, the sad thing about this ticketing software is it doesn’t support nginx server officially. However, if you are running Nginx, you can make it work.
Nginx configuration for OsTicket
server_name server_name;
root /path-to-your-root/;
include mime.types;
default_type application/octet-stream;
sendfile on;
charset utf- ;
gzip on;
gzip_types text/plain application/xml text/javascript;
gzip_min_length ;
index index.php index.html index.htm;
set $path_info "";
# Deny access to all files in the include directory
location ~ ^/include {
deny all;
return ;
}
# Deny access to apache .ht* files (nginx doesn't use these)
location ~ /\.ht {
deny all;
}
# Requests to /api/* need their PATH_INFO set, this does that
if ($request_uri ~ "^/api(/[^\?]+)") {
set $path_info $ ;
}
# /api/*.* should be handled by /api/http.php if the requested file does not exist
location ~ ^/api/(tickets|tasks)(.*)$ {
try_files $uri $uri/ /api/http.php;
}
# /scp/ajax.php needs PATH_INFO too, possibly more files need it hence the .*\.php
if ($request_uri ~ "^/scp/.*\.php(/[^\?]+)") {
set $path_info $ ;
}
# Make sure requests to /scp/ajax.php/some/path get handled by ajax.php
location ~ ^/scp/ajax.php/(.*)$ {
try_files $uri $uri/ /scp/ajax.php;
}
# Set index.php as our directoryindex
location / {
index index.php;
}
# Send php files off to the PHP-FPM listing on localhost:
location ~ \.php$ {
try_files $uri = ;
fastcgi_pass php;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $path_info;
include fastcgi_params;
}
# Make sure requests to /ajax.php/some/path get handled by ajax.php
location ~ ^/ajax.php/(.*)$ {
try_files $uri $uri/ /ajax.php;
}
How to fix file upload undefined 400 in OSTicket
Kudos to this original post: https://github.com/osTicket/osTicket-1.7/issues/538#issuecomment-16049117
If you/your users have problem uploading files when opening a ticket like this:
Then, open the file include/class.osticket.php and find the function get_path_info. Replace its definition with the block above.