A simple RESTful service in native PHP. $_SERVER - Information about the server and execution environment What our RESTful service should be able to do

The possibilities of effectively organizing, searching and distributing information have long been of interest to specialists in the field of computer technology. Since information is mainly text consisting of alphanumeric characters, the development of means for searching and processing information according to patterns that describe text has become the subject of serious theoretical research.

Pattern search allows you not only to find specific text fragments, but also to replace them with other fragments. One standard example of pattern matching is the find/replace commands in text editors—for example, MS Word, Emacs, and my favorite editor, vi. All UNIX users are familiar with programs such as sed, awk, and grep; The richness of these programs' capabilities is largely due to their pattern search capabilities. Pattern search mechanisms solve four main problems:

  • search for strings that exactly match a given pattern;
  • searching for string fragments matching a given pattern;
  • replacing strings and substrings using a pattern;
  • search for strings with a given pattern does not match.

The advent of the Web has created a need for faster and more efficient data search tools that allow users around the world to find the information they need across billions of Web pages. Search engines, online financial services and e-commerce sites would all be completely useless without the means to analyze the enormous volumes of data in these sectors. Indeed, string information processing tools are a vital component of almost any sector that is in one way or another connected with modern information technologies. This chapter focuses on string processing facilities in PHP. We'll look at some standard string functions (there are more than 60 of them in the language!), and the definitions and examples provided will give you the information you need to create web applications. But before we get into the specifics of PHP, I want to introduce you to the basic mechanism that makes pattern searching possible. We are talking about regular expressions.

Regular Expressions

Regular expressions are the basis of all modern pattern matching technologies. A regular expression is a sequence of simple and service characters that describe the searched text. Sometimes regular expressions are simple and clear (for example, the word dog), but often they contain special characters that have a special meaning in regular expression syntax - for example,<(?)>.*<\/.?>.

There are two families of functions in PHP, each of which is a different type of regular expression: POSIX-style or Perl-style. Each type of regular expression has its own syntax and is discussed in the appropriate part of the chapter. Numerous textbooks have been written on this topic and can be found both on the Web and in bookstores. Therefore, I will provide only basic information about each type, and you can find further information on your own if you wish. If you're not already familiar with how regular expressions work, be sure to read the short introductory tutorial that takes up the rest of this section. And if you are well versed in this area, feel free to move on to the next section.

Regular expression syntax (POSIX)

The structure of POSIX regular expressions is somewhat reminiscent of the structure of typical mathematical expressions - different elements (operators) are combined with each other to form more complex expressions. However, it is the sense of combining elements that makes regular expressions such a powerful and expressive tool. The capabilities are not limited to searching for literal text (such as a specific word or number); you can search for strings with different semantics but similar syntax -- for example, all HTML tags in a file.

The simplest regular expression matches a single literal character -- for example, the expression g matches strings such as g , haggle and bag. An expression resulting from concatenating multiple literal characters matches according to the same rules -- for example, the sequence gan matches in any string containing those characters (for example, gang, organize, or Reagan).

Operator | (vertical bar) tests whether one of several alternatives matches. For example, the regular expression php | zend checks the string for php or zend.

Square brackets

Square brackets () have a special meaning in the context of regular expressions -- they mean "any character listed within the brackets." Unlike a php regular expression, which matches on all lines containing php literal text, the expression matches on any line containing the characters p or h. Square brackets play an important role when working with regular expressions, since the search process often involves the task of finding characters from a given interval. Below are some commonly used intervals:

  • -- matches any decimal digit from 0 to 9;
  • -- matches any lowercase character from a to z;
  • -- matches any uppercase character from A to Z;
  • -- matches any lowercase or uppercase character from a to z.

Of course, the intervals listed above merely demonstrate the general principle. For example, you can use spacing to represent any decimal digit from 0 to 3, or spacing to represent any lowercase character from b to v. In short, the intervals are determined completely arbitrarily.

Quantifiers

There is a special class of service characters that indicate the number of repetitions of a single character or construct enclosed in square brackets. These special characters (+, * and (...)) are called quantifiers. The principle of their operation is easiest to explain with examples:

  • p+ means one or more p characters in a row;
  • p* means zero or more p characters in a row;
  • R? means zero or one character p;
  • p(2) means two p characters in a row;
  • p(2,3) means two to three p characters in a row;
  • p(2,) means two or more p characters in a row.

Other service characters

The service characters $ and ^ do not match symbols, but specific positions in the string. For example, the expression p$ means a string that ends with the character p, and the expression ^p means a string that begins with the character p.

  • The construction [^a-zA-Z] matches any character not included at the indicated intervals (a-z and A-Z).
  • Service symbol. (dot) means "any character". For example, the expression p.p matches the character p, followed by an arbitrary character, followed by the character p again.

Combining service symbols results in more complex expressions. Let's look at a few examples:

  • ^.(2)$ -- any string containing smooth two characters;
  • (.*)-- an arbitrary sequence of characters between<Ь>And(probably HTML tags to display bold text);
  • p(hp)* -- the character p followed by zero or more instances of the sequence hp (for example, phphphp).

Sometimes you need to find special characters in strings instead of using them in the special context described. To do this, service characters are escaped with a backslash (\). For example, to search for a dollar amount of money, you can use the expression \$+, that is, “a dollar sign followed by one or more decimal digits.” Note the backslash before the $. Possible matches for this regular expression are $42, $560, and $3.

Standard interval expressions (character classes)

For ease of programming, the POSIX standard has defined some standard interval expressions, also called character classes(character classes). A character class defines one character from a given range -- for example, a letter of the alphabet or a number:

  • [[:alpha:]] -- alphabetic character (aA-zZ);
  • [[:digit:]]-digit (0-9);
  • [[:alnum:]] -- alphabetic character (aA-zZ) or number (0-9);
  • [[:space:]] -- whitespace (newlines, tabs, etc.).

PHP functions for working with regular expressions (POSIX-compliant)

PHP currently supports seven search functions using POSIX-style regular expressions:

  • ereg();
  • ereg_replace();
  • eregi();
  • eregi_replace();
  • split();
  • split();
  • sql_regcase().

Descriptions of these functions are provided in the following sections.

The ereg() function searches a given string for a match for a pattern. If a match is found, TRUE is returned, otherwise FALSE is returned. The syntax of the ereg() function is:

int ereg (string pattern, string string [, array matches])

The search is carried out taking into account the case of alphabetic characters. An example of using ereg() to search in domains.com strings:

$is_com - ereg("(\.)(com$)", $email):

// Function returns TRUE if $email ends with ".com" characters

// In particular, the search will succeed for strings

// "www.wjgilmore.com" and " [email protected]"

Note that due to the presence of the $ symbol, the regular expression only matches if the string ends with .com characters. For example, it will match the string "www.apress.com", but won't match in the line "www.apress.com/catalog".

The optional match parameter contains an array of matches for all subexpressions enclosed in parentheses in the regular expression. Listing 8.1 shows how to use this array to split a URL into multiple segments.

Listing 8.1. Printing elements of the $regs array

$url = "http://www.apress.com";

// Split $url into three components: "http://www". "apress" and "com"

$www_url = ereg("^(http://www)\.([[:alnum:]+\.([[:alnum:]]+)". $url, $regs);

if ($www_url) : // If $www_url contains a URL

echo $regs; // The entire string "http://www.apress.com"

print "
";

echo $regs[l]; // "http://www"

print "
";

echo $regs; // "apress"

print "
";

echo $regs; // "com" endif;

Running the script in Listing 8.1 produces the following output:

http://www.apress.com http://www apress com

The ereg_replace() function searches a given string for a match for a pattern and replaces it with a new fragment. Syntax of the ereg_replace() function:

string ereg_replace (string pattern, string replacement, string string)

The ereg_replace() function works on the same principle as ereg(), but its capabilities are expanded from simple search to search with replacement. After performing the replacement, the function returns the modified string. If matches

are missing, the line remains in the same state. The ereg_replace() function, like ereg(), is case sensitive. Below is a simple example demonstrating the use of this function:

$copy_date = "Copyright 1999":

$copy_date = ereg_replace("(+)". "2000", $copy_date);

print $copy_date: // Prints the string "Copyright 2000"

One interesting feature of PHP's search and replace tools is the ability to use backreferences to parts of the main expression enclosed in parentheses. Backreferences are similar to the elements of the optional match array parameter of ereg(), with one exception: backreferences are written as \0, \1, \2, etc., where \0 matches the entire string, \1 matches a successful match first subexpression, etc. The expression can contain up to 9 backlinks. The following example replaces all URL links in the text with working hyperlinks:

$url = "Apress (http://www.apress.com");

$url = ereg_replace("http://(()*)", " \\0", $url);

// The line is displayed:

// Apress (http://www.apress.com)

The eregi() function searches a given string for a match for a pattern. Syntax of the eregi() function:

int eregi (string pattern, string string [, array matches])

The search is in progress excluding case of alphabetic characters. The eregi() function is especially useful for checking the validity of entered strings (for example, passwords). The use of the eregi() function is demonstrated in the following example:

$password = "abc";

if (! eregi("[[:alnum:]](8.10), $password) :

print "Invalid password! Passwords must be from 8 through 10 characters in length.";

// As a result of executing this fragment, an error message is displayed.

// because the length of the string "abc" is not within the allowed interval

// from 8 to 10 characters.

The eregi_replace() function works exactly the same as ereg_replace(), with one exception: the search is case-insensitive. Syntax of the ereg_replace() function:

string eregi_replace (string template, string replacement, string string)

The split() function splits a string into elements whose boundaries are determined by a given pattern. The split() function syntax is:

array split (string pattern, string string [, int threshold])

The optional threshold parameter specifies the maximum number of elements into which the string is divided from left to right. If the pattern contains alphabetic characters, the spl it() function is case sensitive. The following example demonstrates the use of the split() function to split a canonical IP address into triplets:

$ip = "123.345.789.000"; // Canonical IP address

$iparr = split ("\.", $ip) // Because the dot is a service character.

// it must be escaped.

print "$iparr
"; // Outputs "123"

print "$iparr
"; // Outputs "456"

print "$iparr
"; // Outputs "789"

print "$iparr
"; // Outputs "000"

The spliti() function works exactly like its prototype split(), with one exception: it does not take into account character register. The syntax of the spliti() function is:

array spliti (string pattern, string string [, int threshold])

Of course, case is only important if the pattern contains alphabetic characters. For other characters, performing spliti() is exactly the same as split().

The sql_regcase() helper function encloses each character in the input string in square brackets and appends the matching character to it. Syntax of the sql_regcase() function:

string sql_regcase (string string)

If an alphabetic character exists in two variants (upper and lower case), the expression in square brackets will contain both variants; otherwise, the original character is repeated twice. The sql_regcase() function is especially useful when using PHP with software packages that support single-case regular expressions. An example of converting a string using the sql_regcase() function:

$version = "php 4.0";

print sql_regcase($version);

// Displays the string [..]

Perl-style regular expression syntax

Converting a string to upper and lower case

There are four functions in PHP designed to change the case of a string:

  • strtolower();
  • strtoupper();
  • ucfirst();
  • ucwords().

All these features are described in detail below.

strtolower()

The strtolower() function converts all alphabetic characters in a string to lowercase. The strtolower() function syntax is:

string strtolower(string string)

Non-alphabetic characters are not changed by the function. Converting a string to lowercase using the strtolower() function is demonstrated in the following example:

$sentence = strtolower($sentence);

// "cooking and programming php are my two favorite pastimes!"

Strings can be converted not only to lower case, but also to upper case. The conversion is performed by the strtoupper() function, which has the following syntax:

string strtoupper (string string)

Non-alphabetic characters are not changed by the function. Converting a string to uppercase using strtoupper() is demonstrated in the following example:

$sentence = "cooking and programming PHP are my two favorite pastimes!";

$sentence = strtoupper($sentence);

// After calling the function $sentence contains the string

// "COOKING AND PROGRAMMING PHP ARE MY TWO FAVORITE PASTIMES!"

The ucfirst() function converts the first character of a string to uppercase, provided it is an alphabetic character. ucfirst() function syntax:

string ucfirst (string string)

Non-alphabetic characters are not changed by the function. The conversion of the first character of a string by the ucfirst() function is demonstrated in the following example:

&sentence = "cooking and programming PHP are my two favorite pastimes!";

$sentence = ucfirst($sentence);

// After calling the function $sentence contains the string

// "Cooking and programming PHP are my two favorite pastimes!"

The ucwords() function converts the first letter of each word in a string to uppercase. Syntax of the ucwords() function:

string ucwords (string string")

Non-alphabetic characters are not changed by the function. A "word" is defined as a sequence of characters separated from other string elements by spaces. The following example demonstrates how the ucwords() function converts the first characters of words:

$sentence = "cooking and programming PHP are my two favorite pastimes!";

$sentence = ucwords($sentence);

// After calling the function $sentence contains the string

// "Cooking And Programming PHP Are My Two Favorite Pastimes!"

Project: Browser Identification

Every programmer trying to create a user-friendly website must take into account differences in page formatting when viewing the site in different browsers and operating systems. Although the W3 consortium (http://www.w3.org) continues to publish standards that programmers must adhere to when creating web applications, browser developers like to add their own little “improvements” to these standards, which ultimately causes chaos and confusion. Developers often solve this problem by creating different pages for each type of browser and operating system - this increases the workload significantly, but the resulting site is ideal for every user. The result is a good reputation for the site and confidence that the user will visit it again.

To allow the user to view the page in a format that is appropriate for their browser and operating system, information about the browser and platform is extracted from the incoming page request. After receiving the necessary data, the user is redirected to the desired page.

The project below (sniffer.php) shows how to use PHP functions to work with regular expressions to retrieve information from queries. The program determines the type and version of the browser and operating system, and then displays the received information in the browser window. But before moving on to the actual analysis of the program, I want to introduce one of its main components - the standard PHP variable $HTTP_USER_AGENT. This variable stores various information about the user's browser and operating system in string format—exactly what we are interested in. This information can be easily displayed on the screen with just one command:

echo $HTTP USER_AGENT;

When running Internet Explorer 5.0 on a Windows 98 computer, the result will look like this:

Mozilla/4.0 (compatible; MSIE 5.0; Windows 98; DigExt)

For Netscape Navigator 4.75 the following data is displayed:

Mozilla/4.75 (Win98; U)

Sniffer.php extracts the necessary data from $HTTP_USER_AGENT using string processing and regular expression functions. Program algorithm in pseudocode:

  • Define two functions to identify the browser and operating system: browser_info() and opsys_info(). Let's start with the pseudocode of the browser_info() function.
  • Determine the browser type using the eged() function. Although this function is slower than simplified string functions like strstr(), it is more convenient in this case because the regular expression allows you to determine the browser version.
  • Use the if/elseif construct to identify the following browsers and their versions: Internet Explorer, Opera, Netscape, and a browser of unknown type.
  • Return browser type and version information as an array.
  • The opsys_info() function determines the operating system type. This time the strstr() function is used, since the OS type is determined without using regular expressions.
  • Use the if/elseif construct to identify the following systems: Windows, Linux, UNIX, Macintosh, and unknown operating system.
  • Return information about the operating system.

Listing 8.3. Identifying the browser type and client operating system

File: sniffer.php

Purpose: Identification of browser type/version and platform

// Function: browser_info

// Purpose: Returns browser type and version

function browser_info ($agent) (

// Determine browser type

// Search for Internet Explorer signature

if (ereg("MSIE (.(1,2))", $agent, $version))

$browse_type = "IE";

$browse version = $version;

// Search for Opera signature

elseif (ereg("Opera (.(1,2))". $agent, $version)):

$browse_type = "Opera":

$browse_version = $version:

// Search for Netscape signature. Checking the Netscape Browser

// *must* be done after checking Internet Explorer and Opera,

// because all these browsers like to report the name

// Mozilla along with real name.

elseif (ereg("Mozilla/(.(1,2))". $agent, $version)) :

$browse_type = "Netscape";

$browse_version = $version;

// Unless it's Internet Explorer, Opera or Netscape.

// means we have detected an unknown browser,

$browse_type = "Unknown";

$browse_version = "Unknown";

// Return browser type and version as an array

return array ($browse_type, $browse_version);

) // End of browser_info function

// Function: opsys_info

// Purpose: Returns information about the user's operating system

function opsys_info($agent) (

// Identify the operating system

// Search for Windows signature

if (strstr($agent. "win")) :

$opsys = "windows";

// Search for Linux signature

elseif (strstr($agent, "Linux")) :

$opsys = "Linux";

// Search for UNIX signature

elseif (strstr (Sagent, "Unix")) :

$opsys = "Unix";

// Look for Macintosh signature

elseif (strstr($agent, "Mac")) :

$opsys = "Macintosh";

// Unknown platform else:

$opsys = "Unknown";

// Return information about the operating system

list ($browse_type. $browse_version) = browser_info ($HTTP_USER_AGENT); Soperating_sys = opsysjnfo($HTTP_USER_AGENT);

print "Browser Type: $browse_type
";

print "Browser Version: $browse_version
";

print "Operating System: $operating_sys
":

That's all! For example, if the user is using the Netscape 4.75 browser on a Windows computer, the following output will be displayed:

Browser Type: Netscape

Browser Version: 4.75

Operating System: Windows

In the next chapter, you'll learn how to navigate between pages and even create style sheets for specific operating systems and browsers.

Results

This chapter covered quite a lot of material. What good is a programming language if you can't work with text in it? We covered the following topics:

  • general information about regular expressions in POSIX and Perl styles;
  • standard PHP functions for working with regular expressions;
  • changing line length;
  • determining the length of a string;
  • alternative PHP functions for processing string information;
  • converting plain text to HTML and vice versa;
  • changing the case of characters in strings.

The next chapter opens the second part of the book - by the way, my favorite. In it, we'll begin to explore PHP's Web-centric tools, looking at the process of dynamically creating content, including files, and building common templates. Later chapters in Part 2 cover working with HTML forms, databases, session data tracking, and advanced template tools. Hang in there - the fun is about to begin!

Contents

$user_info

These are all the keys defined for $user_info in loadUserSettings(). Some are self-explanatory, for sure.

groups

array. All possible membergroups attached too a user. Keys don't matter. Values ​​are the groups, sanitized as int, just in case. This includes:

  • Primary group
  • Post count group
  • Additional groups. These are stored in the database as a comma-delimited string.

possibly_robot

bool. Is true if the agent matches a known spider if the feature is enabled, and if disabled, makes an educated guess.

id

int Corresponds to the member"s database value "id_member".!}

username

name

string. Their displayed name.

email

passwd

language

is_guest

is_admin

theme

last_login

int. Unix timestamp.

ip

$_SERVER["REMOTE_ADDR"]

ip2

$_SERVER["BAN_CHECK_IP"]

posts

int. Post count.

time_format

string. Custom format for strtftime().

time_offset

int. User's hourly offset from the forum time.

avatar

  • url:string
  • filename:string
  • custom_dir: bool
  • id_attach: int

smiley_set

messages

int. Number of PMs they have.

unread_messages

int. Number of unread PMs they have.

total_time_logged_in

buddies

String. The list of their buddies delimited by commas.

ignoreboards

String. The list of boards they have ignored delimited by commas.

ignoreusers

String. The list of users they have opted to ignore delimited by commas.

  • In SMF 1.1.x only PMs get ignored.
  • In SMF 2.0.x and onwards, this feature has been improved to also hide the posts of the ignored user.

warning

int. Their warning points.

permissions

query_see_board

The list of all the boards they can see as part of a prepared SQL statement.

query_wanna_see_board

The list of boards they WANT to see as part of a prepared SQL statement.

mod_cache

is_mod

Boolean. Is false except in boards where the member is a moderator. It is always set.

Those who have more or less seriously studied PHP know that there is one very useful global array in PHP which is called $_SERVER. And in this article I would like to analyze the most popular keys and their values ​​in this array, since knowledge of them is simply mandatory even for a beginner PHP programmer.

Before you start global array $_SERVER in PHP, I’ll give you a little hint right away. There is a great feature built into PHP, which is called phpinfo(). Let's immediately give an example of its use:

phpinfo();
?>

As a result of executing this simple script, you will see a huge table with various PHP interpreter settings, including, near the end there will be a table of values global array $_SERVER. It will list all the keys and all their corresponding values. How can this help you? And the fact is that if you need this or that value, and you forget what the key is called, then using the function phpinfo() You can always remember its name. In general, you will execute this script and you will immediately understand me.

Now let's move on to the most popular to the keys of the $_SERVER array:

  • HTTP_USER_AGENT- this key allows you to find out the client’s characteristics. In most cases, this is certainly the browser, however, not always. And again, if it’s a browser, then which one, you can find out about it in this variable.
  • HTTP_REFERER- contains the absolute path to that file ( PHP script, HTML page), from which we switched to this script. Roughly speaking, where the client came from.
  • SERVER_ADDR - IP address server.
  • REMOTE_ADDR - IP address client.
  • DOCUMENT_ROOT- physical path to the root directory of the site. This option is set via Apache server configuration file.
  • SCRIPT_FILENAME- physical path to the called script.
  • QUERY_STRING- a very useful value that allows you to get a string with a request, and then you can parse this string.
  • REQUEST_URI- an even more useful value that contains not only the request itself, but also the relative path to the called script from the root. This is very often used to remove duplication from index.php, that is, when we have such URL: "http://mysite.ru/index.php" And " http://mysite.ru/" lead to one page, and URLs different, hence duplication, which will have a bad effect on search engine optimization. And with the help REQUEST_URI we can determine: with index.php or not the script was called. And we can do a redirect with index.php(if he was present in REQUEST_URI) on without index.php. As a result, when sending such a request: " http://mysite.ru/index.php?id=5", we will have a redirect to URL: "http://mysite.ru/?id=5". That is, we got rid of duplication by removing from URL this index.php.
  • SCRIPT_NAME- relative path to the called script.

Perhaps these are all the elements global array $_SERVER in PHP that are used regularly. You need to know them and be able to use them when necessary.

February 5 , 2017

I don't know any PHP framework. This is sad and shameful, but it is not prohibited by law yet. But at the same time I want to play with the REST API. The problem is that php by default only supports $_GET and $_POST. And for a RESTful service you also need to be able to work with PUT, DELETE and PATCH. And it is not very obvious how to culturally process many requests like GET http://site.ru/users, DELETE http://site.ru/goods/5 and other obscenities. How to wrap all such requests into a single point, universally parse them into parts and run the necessary code to process the data?

Almost any PHP framework can do this out of the box. For example, Laravel, where routing is implemented clearly and simply. But what if we don’t need to study a new big topic right now, but just want to quickly start a project with REST API support? This will be discussed in the article.

What should our RESTful service be able to do?

1. Support all 5 main request types: GET, POST, PUT, PATCH, DELETE.
2. Solve various routes of the view
POST /goods
PUT /goods/(goodId)
GET /users/(userId)/info
and other arbitrarily long chains.

Attention: this article is not about the basics of the REST API
I assume that you are already familiar with the REST approach and understand how it works. If not, then there are many great articles on the Internet on the basics of REST - I don’t want to duplicate them, my idea is to show how to work with REST in practice.

What functionality will we support?

Let's consider 2 entities - products and users.

For products, the options are as follows:

  • 1. GET /goods/(goodId)— Obtaining information about the product
  • 2. POST /goods— Adding a new product
  • 3. PUT /goods/(goodId)— Editing a product
  • 4. PATCH /goods/(goodId)— Editing some product parameters
  • 5. DELETE /goods/(goodId)— Removing a product

For users, for variety, let’s consider several options with GET

  • 1. GET /users/(userId)— Complete information about the user
  • 2. GET /users/(userId)/info— Only general information about the user
  • 3. GET /users/(userId)/orders— List of user orders

How will this work with native PHP?

The first thing we'll do is set up .htaccess so that all requests are redirected to the index.php file. It is he who will be engaged in data extraction.

Second, let's decide what data we need and write the code to get it - in index.php.
We are interested in 3 types of data:

  • 1. Request method (GET, POST, PUT, PATCH or DELETE)
  • 2. Data from the URL, for example, users/(userId)/info - all 3 parameters are needed
  • 3. Data from the request body
And third, we’ll write code that runs the necessary functions. The functions are divided into files, everything is Feng Shui, adding new paths and methods for a RESTful service will be very simple.

.htaccess

Let's create a .htaccess file in the project root

RewriteEngine On RewriteCond %(REQUEST_FILENAME) !-f RewriteRule ^(.+)$ index.php?q=$1

With these mysterious lines we command you to do this:
1 - send all requests of any kind to the king-file index.php
2 - make the string in the URL available in index.php in the get parameter q. That is, data from a URL like /users/(userId)/info we will get from $_GET["q"].

index.php

Let's look at index.php line by line. First, let's get the request method.

// Define the request method $method = $_SERVER["REQUEST_METHOD"];

Then the data from the request body

// Get data from the request body $formData = getFormData($method);

For GET and POST, it's easy to pull data from the corresponding $_GET and $_POST arrays. But for other methods you need to be a little perverted. The code for them is pulled from the stream php://input, the code is easy to Google, I just wrote a general wrapper - the getFormData($method) function

// Getting data from the request body function getFormData($method) ( // GET or POST: return data as is if ($method === "GET") return $_GET; if ($method === "POST") return $_POST; // PUT, PATCH or DELETE $data = array(); explode("&", file_get_contents("php://input")); foreach($exploded as $pair) ( $item = explode("=", $pair); if (count($item) == 2) ( $data = urldecode($item); ) ) return $data;

That is, we got the necessary data by hiding all the details in getFormData - well, great. Let's move on to the most interesting part - routing.

// Parse the url $url = (isset($_GET["q"])) ? $_GET["q"] : ""; $url = rtrim($url, "/"); $urls = explode("/", $url);

We learned above that .htaccess will put parameters from the URL in the q-parameter of the $_GET array. That is, $_GET["q"] will contain something like this: users/10. Regardless of which method we use to execute the request.

A explode("/", $url) converts this string into an array for us, which we can already work with. Thus, make as long chains of queries as you like, for example,
GET /goods/page/2/limit/10/sort/price_asc
And rest assured, you will receive an array

$urls = array("goods", "page", "2", "limit", "10", "sort", "price_asc");

Now we have all the data, we need to do something useful with it. And just 4 lines of code will do it

// Define the router and url data $router = $urls; $urlData = array_slice($urls, 1); // Connect the router file and run the main function include_once "routers/" . $router. ".php"; route($method, $urlData, $formData);

Do you get it? We create a routers folder in which we put files that manipulate one entity: products or users. At the same time, we agree that the names of the files coincide with the first parameter in urlData - it will be the router, $router. And this router needs to be removed from urlData, we no longer need it and is only used to connect the required file. array_slice($urls, 1) and will give us all the elements of the array except the first one.

Now all that remains is to connect the desired router file and run the route function with three parameters. What is this function route? Let's agree that in each router file a function will be defined that, based on the input parameters, will determine what action the user initiated and will execute the required code. Now this will become clearer. Let's consider the first request - obtaining data about a product.

GET /goods/(goodId)

File routers/goods.php

// Router function route($method, $urlData, $formData) ( // Getting information about a product // GET /goods/(goodId) if ($method === "GET" && count($urlData) === 1) ( // Get the product id $goodId = $urlData; // Pull the product out of the database... // Output the response to the client echo json_encode(array("method" => "GET", "id" => $goodId, "good" => "phone", "price" => 10000)); return; // Return error header("HTTP/1.0 400 Bad Request"); echo json_encode(array("error" => "Bad Request") ")); )

The contents of the file are one large route function, which, depending on the passed parameters, performs the necessary actions. If the GET method and 1 parameter (goodId) is passed to urlData, then this is a request to obtain data about a product.

Attention: the example is very simplified
In real life, of course, you need to additionally check the input parameters, for example, that goodId is a number. Instead of writing the code here, you'll probably include the required class. And to receive the product, create an object of this class and call some method on it.
Or maybe transfer control to some controller, which will already take care of initializing the necessary models. There are many options, we are only considering the general structure of the code.

In the response to the client, we display the necessary data: the name of the product and its price. The product id and method are completely optional in a real application. We show them only to make sure that the correct method is called with the correct parameters.

Let's try it with an example: open your browser console and run the code

$.ajax((url: "/examples/rest/goods/10", method: "GET", dataType: "json", success: function(response)(console.log("response:", response))) )

The code will send a request to the server where I have deployed a similar application and output a response. Make sure the route you are interested in is /goods/10 really worked. On the Network tab you will notice the same request.
And yes, /examples/rest is the root path of our test application to the site

If you are more accustomed to using curl in the console, then run this in the terminal - the answer will be the same, and even with headers from the server.

Curl -X GET https://site/examples/rest/goods/10 -i

At the end of the function we wrote the following code.

// Return an error header("HTTP/1.0 400 Bad Request"); echo json_encode(array("error" => "Bad Request"));

It means that if we made a mistake with the parameters or the requested route is not defined, we will return the 400 Bad Request error to the client. For example, add something like this to the URL: goods/10/another_param and you will see an error in the console and a 400 response - the crooked request did not go through.

By server response http codes
We won’t bother with outputting different codes, although this is worth doing according to REST. There are many client errors. Even in our simple case, a 405 is appropriate in case of an incorrectly passed method. I don't want to complicate things on purpose.
If successful, our server will always return 200 OK. Fortunately, when creating a resource, you should give 201 Created. But again, in terms of simplification, we will discard these subtleties, but in a real project you can easily implement them yourself.

In all honesty, the article is finished. I think you already understand the approach, how all routes are resolved, data is retrieved, how to test it, how to add new requests, etc. But to complete the image, I will give the implementation of the remaining 7 queries that we outlined at the beginning of the article. Along the way, I’ll make a couple of interesting comments, and at the end I’ll post an archive with the source code.

POST /goods

Adding a new product

// Adding a new product // POST /goods if ($method === "POST" && empty($urlData)) ( // Adding a product to the database... // Output the response to the client echo json_encode(array("method" => "POST", "id" => rand(1, 100), "formData" => $formData));

urlData is now empty, but formData is used - we will simply display it to the client.

How to do it "right"?
According to the canons of REST, in a post request you should return only the id of the created entity or the url by which this entity can be obtained. That is, the answer will either be just a number - (goodId), or /goods/(goodId).
Why did I write "correctly" in quotes? Yes, because REST is not a set of strict rules, but recommendations. And how you will implement it depends on your preferences or already accepted agreements on a specific project.
Just keep in mind that another programmer reading the code and aware of the REST approach will expect in the response to a post request the id of the created object or a url from which data about this object can be retrieved with a get request.

Testing from the console

$.ajax((url: "/examples/rest/goods/", method: "POST", data: (good: "notebook", price: 20000), dataType: "json", success: function(response)( console.log("response:", response))))

Curl -X POST https://site/examples/rest/goods/ --data "good=notebook&price=20000" -i

PUT /goods/(goodId)

Editing a product

// Update all product data // PUT /goods/(goodId) if ($method === "PUT" && count($urlData) === 1) ( // Get the product id $goodId = $urlData; // Update all product fields in the database... // Output the response to the client echo json_encode(array("method" => "PUT", "id" => $goodId, "formData" => $formData));

Here all the data is already used to its fullest. The product id is pulled out from urlData, and properties are pulled out from formData.

Testing from the console

$.ajax((url: "/examples/rest/goods/15", method: "PUT", data: (good: "notebook", price: 20000), dataType: "json", success: function(response) (console.log("response:", response))))

Curl -X PUT https://site/examples/rest/goods/15 --data "good=notebook&price=20000" -i

PATCH /goods/(goodId)

Partial product update

// Partial update of product data // PATCH /goods/(goodId) if ($method === "PATCH" && count($urlData) === 1) ( // Get the product id $goodId = $urlData; // We update only the specified product fields in the database... // Output the response to the client echo json_encode(array("method" => "PATCH", "id" => $goodId, "formData" => $formData));

Testing from the console

$.ajax((url: "/examples/rest/goods/15", method: "PATCH", data: (price: 25000), dataType: "json", success: function(response)(console.log(" response:", response))))

Curl -X PATCH https://site/examples/rest/goods/15 --data "price=25000" -i

Why all this show-off with PUT and PATCH?
Isn't one PUT enough? Don't they perform the same action - update the object's data?
That's right - outwardly the action is one. The difference is in the transmitted data.
PUT assumes that All object fields, and PATCH - only changed. Those sent in the request body. Please note that in the previous PUT we passed both the product name and the price. And in PATCH - only the price. That is, we sent only changed data to the server.
Whether you need PATCH - decide for yourself. But remember that code-reading programmer I mentioned above.

DELETE /goods/(goodId)

Removing a product

// Deleting a product // DELETE /goods/(goodId) if ($method === "DELETE" && count($urlData) === 1) ( // Get the product id $goodId = $urlData; // Delete the product from the database... // Output the response to the client echo json_encode(array("method" => "DELETE", "id" => $goodId));

Testing from the console

$.ajax((url: "/examples/rest/goods/20", method: "DELETE", dataType: "json", success: function(response)(console.log("response:", response))) )

Curl -X DELETE https://site/examples/rest/goods/20 -i

Everything is clear with the DELETE request. Now let's look at working with users - the users router and, accordingly, the users.php file

GET /users/(userId)

Retrieving all user data. If the GET request is like /users/(userId), then we will return all information about the user if additionally specified /info or /orders, then, accordingly, only general information or a list of orders.

// Router function route($method, $urlData, $formData) ( // Getting all information about the user // GET /users/(userId) if ($method === "GET" && count($urlData) == = 1) ( // Get the product id $userId = $urlData; // Extract all data about the user from the database... // Output the response to the client echo json_encode(array("method" => "GET", "id" = > $userId, "info" => array("email" => " [email protected]", "name" => "Webdevkin"), "orders" => array(array("orderId" => 5, "summa" => 2000, "orderDate" => "01/12/2017"), array(" orderId" => 8, "summa" => 5000, "orderDate" => "02/03/2017")))); return; ) // Return the error header("HTTP/1.0 400 Bad Request"); echo json_encode( array("error" => "Bad Request"));

Testing from the console

$.ajax((url: "/examples/rest/users/5", method: "GET", dataType: "json", success: function(response)(console.log("response:", response))) )

Curl -X GET https://site/examples/rest/users/5 -i

GET /users/(userId)/info

General information about the user

// Getting general information about the user // GET /users/(userId)/info if ($method === "GET" && count($urlData) === 2 && $urlData === "info") ( / / Get the product id $userId = $urlData; // Extract general data about the user from the database... // Output the response to the client echo json_encode(array("method" => "GET", "id" => $userId, " info" => array("email" => " [email protected]", "name" => "Webdevkin"))); return; )

Testing from the console

$.ajax((url: "/examples/rest/users/5/info", method: "GET", dataType: "json", success: function(response)(console.log("response:", response) )))

Curl -X GET https://site/examples/rest/users/5/info -i

GET /users/(userId)/orders

Getting a list of user orders

// Receive user orders // GET /users/(userId)/orders if ($method === "GET" && count($urlData) === 2 && $urlData === "orders") ( // Get product id $userId = $urlData; // Retrieve data about the user's orders from the database... // Output the response to the client echo json_encode(array("method" => "GET", "id" => $userId, "orders" => array(array("orderId" => 5, "summa" => 2000, "orderDate" => "01/12/2017"), array("orderId" => 8, "summa" => 5000, "orderDate " => "02/03/2017")))); return; )

Testing from the console

$.ajax((url: "/examples/rest/users/5/orders", method: "GET", dataType: "json", success: function(response)(console.log("response:", response) )))

Curl -X GET https://site/examples/rest/users/5/orders -i

Results and sources

Sources from article examples -

As you can see, organizing REST API support in native php turned out to be not so difficult and in completely legal ways. The main thing is support for routes and non-standard PHP methods PUT, PATCH and DELETE.

The main code that implements this support fits into 3 dozen lines of index.php. The rest is just a harness that can be implemented in any way you like. I suggested doing this in the form of plug-in router files, the names of which match the entities of your project. But you can use your imagination and find a more interesting solution.

Gets a WP_User object that contains all of the specified user's data.

The data returned by the functions fully corresponds to the fields of the database tables: wp_users and wp_usermeta (table description).

This is a pluggable feature - i.e. it can be replaced from the plugin. This means that it will work (connect) only after all plugins have been connected, and until this point the function has not yet been defined... Therefore, you cannot call this and the functions that depend on it directly from the plugin code. They need to be called via the plugins_loaded hook or later, such as the init hook.

Function replacement (override) - in the plugin you can create a function with the same name, then it will replace the current function.

✈ 1 time = 0.000296s = fast| 50000 times = 0.78s = very fast| PHP 7.1.2RC1, WP 4.7.2

There are no hooks.

Returns

WP_User/false. Data object, or false if the specified user could not be found.

Usage

get_userdata($userid); $userid (number) (required) ID of the user whose data you want to retrieve.
Default: no

Examples

#1 How to output data from a received data object

$user_info = get_userdata(1); echo "Username: " . $user_info->user_login . "\n"; echo "Access level: " . $user_info->user_level . "\n"; echo "ID: " . $user_info->ID . "\n"; /* Outputs: User name: admin Access level: 10 ID: 1 */

#1.2 Data into a variable

Another example, only here we will first write the data into variables, and then display it on the screen:

$user = get_userdata(1); $username = $user->user_login; $first_name = $user->first_name; $last_name = $user->last_name; echo "$first_name $last_name visited the site under the login: $username."; /* Object $user: WP_User Object( => stdClass Object( => 80 => kogian => $P$BJFHKJfUKyWv1TwLOVAENYU0JGNsq. => kogian => [email protected]=> http://example.com/ => 2016-09-01 00:34:42 => => => kogian) => 80 => Array( => 1) => wp_capabilities => Array( => subscriber) => Array( => 1 => 1 => 1) => => 1) */

#2 Class methods

The object obtained by get_userdata() is an instance of the class and it has methods that can be used. Sometimes this can come in handy. Here's a simple example of getting a user option using the $user->get() method:

$user = get_userdata(1); echo $username = $user->get("user_login");

List of some methods:

    get($key) - returns the option value;

    has_prop($key) - checks whether the specified option is installed;

    has_cap($cap) - checks whether the user has the specified capability or role;

    get_role_caps() - Gets all the capabilities of the user's role and combines them with the individual capabilities of the user;

    add_role($role) - adds a role to the user;

    remove_role($role) - removes a role from a user;

  • set_role($role) - sets the user role;

Notes

Here are some useful wp_users and wp_usermeta table field values ​​that you can use to retrieve data:

  • display_name

user_meta

  • user_description

    wp_capabilities (array)

    admin_color (admin panel theme. Default - fresh)

    closedpostboxes_page

  • source_domain

It should also be noted that since version 3.2, the returned data has changed slightly: the WP_User object is returned. The data in the object is divided into groups: data, caps, roles (previously the data was returned in a general list).

However, thanks to the "magic" (utility) methods of PHP, data can be obtained as before, for example, now the data is stored like this: get_userdata(1)->data->rich_editing , but you can get it like this: get_userdata(1)->rich_editing , even though var_dump() will not show this relationship.

Code get userdata: wp-includes/pluggable.php WP 5.2.2

document.write("");
mob_info