Taking PHP seriously. Learning PHP Using Output Modifiers with Parameters

A Soyuz rocket delivered by train to the launch pad. NASA public domain photo.

Virtues of PHP

PHP has some very deep and definitely true features.

First, state. Every web request starts with a completely blank slate. Its namespace and global variables are uninitialized, with the exception of some standard global variables, functions, and classes that provide primitive functionality and life support. By starting each request from a known state, we gain a kind of isolation from possible errors; If request t encounters a software problem and fails, the bug has no effect on the execution of subsequent request t+1. In reality, of course, application state resides in other places besides the program heap, and it is quite possible to completely corrupt the database, memcache, or filesystem. But PHP shares this weakness with every conceivable framework that allows stateful persistence. At the same time, sharing software heaps between requests reduces the cost of most software bugs.

Second, parallelism. An individual request runs in one PHP thread. At first glance, this seems like a stupid restriction. But since our application runs in the context of a web server, we have a natural source of concurrency: web requests. By simply sending asynchronous requests to ourselves, we can easily achieve parallelism with isolated states and copy-restore calls. In practice, this is safer and more error-tolerant than the locking and shared state approach used in other general-purpose languages.

In conclusion, the fact that PHP programs operate at the request level means that the programmer's workflow is fast and efficient, and remains fast after the application changes. Plenty of productivity languages ​​claim to do this, but unless they clear their state on every request and the main event thread shares program-level state between requests, they almost always take a while to run. For a typical Python application server, a typical debugging loop would look something like “think, edit, restart server, send some test requests.” Even if “restart server” only takes a few seconds out of the total number of hours, it takes a big chunk out of 15-30 seconds of our human brains on the need to keep the most unnecessary part of the current state in our heads.

I argue that "think, edit, reload" style of PHP development makes developers more productive. In long and complex project development cycles, this provides even greater gains.

The case against PHP

If all this is true, why is he hated so much? When you put all the colorful hyperbole aside, the main complaints about a PHP cluster boil down to the following problems:


Not to sound like an unreflective PHP apologist: these are all serious problems that make it more likely to introduce defects. They are unforced errors. There is no inherent trade-off between the Good Parts of PHP and these issues. It should be possible to create PHP that solves these shortcomings while retaining all the good aspects.

HHVM and Hack

This successor to the PHP system is called Hack

Hack is what people call an "incremental type system" programming language for PHP. "Type system" means that it allows the programmer to make automatically verifiable invariants about the data that flows through the code: a given function takes a string or a number and returns a Fribbles sheet, like in Java or C++ or Haskell or any other statically typed language, whichever one you choose. "Gradual" means that some parts of your codebase may be statically typed, while other parts may still be in messy, dynamic PHP. The ability to combine these approaches allows large codebases to be migrated over time.

Instead of spilling a ton of ink here describing the Hack type system and how it works, just play around with it. I'll be here when you return.

It's a neat system, and it's quite ambitious in what it allows you to express. And having the ability to gradually migrate a project to Hack when it grows larger than you initially expected is a unique benefit of the PHP ecosystem. Hack type checks preserve a "think, edit, reload page" style workflow because they run in the background, gradually updating the codebase model when it sees modifications to the file system. The Hack Project provides integrations with all popular editors and IDEs, so you can see type error feedback as soon as you finish typing, just like in the web demo.

Let's look at the totality of the real risks that PHP creates in the light of Hack:


Hack provides an opportunity that other popular members of the MPDPL family do not have: the ability to introduce a type system after the main development, and only partially, in those parts of the system where the value outweighs the cost.

HHVM

Hack was originally developed as part of the HipHop Virtual Machine, or HHVM, an open-source virtual environment for PHP. HHVM provides another important option for a successful project: the ability to launch your site faster and more economically. Facebook

Filters in Revolution allow you to manipulate how certain tags are processed. They allow you to change values ​​right inside your templates.

Input filters

Currently, input filters are used in preparation for processing output filters. They are usually only used inside the MODX engine.

Output filters

In Revolution, output filters behave the same way as PHx in Evolution, only the filters are now built directly into the MODX engine. The syntax looks like this:

[]

Filters can be applied sequentially. To do this, write them in a row (from left to right):

[]

Filters can also be used to modify the output of snippets. The filter must be specified before all parameters (before the question mark):

[]

Output modifiers

The table shows some modifiers and examples of their use. In the examples, modifiers are applied to placeholders, but you should remember that they can be applied to any MODX tags. Make sure the tag you use outputs at least something that the modifier will process.

Conditional Output Modifiers

Modifier Description Usage example
if, input Pass arbitrary text as input for the next modifier [[*id:input=`[[+placeholder]]`:is=`1`:then=`Yes`:else=`No`]]
or OR [[+numbooks:is=`5`:or:is=`6`:then=`There are 5 or 6 books here`:else=`Not sure how many books`]]
and Combining multiple modifiers with a link AND [[+numbooks:gt=`5`:and:lt=`10`:then=`There are from 5 to 10 books here`:else=`There are either less than 5 or more than 10 books`]]
isequalto, isequal, equalto, equals, is, eq Compares the passed value with the set value. If the values ​​match, the value “then” is displayed, if not - “else” [[+numbooks:isequalto=`5`:then=`There are 5 books here`:else=`Not sure how many books`]]
notequalto, notequals, isnt, isnot, neq, ne Compares the passed value with the set value. If the values ​​do NOT match, the value "then" is displayed, if not - "else" [[+numbooks:notequalto=`5`:then=`Not sure how many books`:else=`There are 5 books`]]
greater thanorequalto, equalorgreaterthen, ge, eg, isgte, gte The same, only the condition is “Greater than or equal to” [[+numbooks:gte=`5`:then=`There are 5 books or more here`:else=`There are less than five books here`]]
isgreaterthan, greaterthan, isgt, gt The same, only the condition “Strictly more” [[+numbooks:gt=`5`:then=`There are more than five books here`:else=`There are 5 books or less`]]
equaltoorlessthan, lessthanorequalto, el, le, islte, lte The same, only the condition is “Less than or equal to” [[+numbooks:lte=`5`:then=`There are 5 books or less here`:else=`There are more than five books here`]]
islowerthan, islessthan, lowerthan, lessthan, islt, lt The same, only the condition “Strictly less” [[+numbooks:lte=`5`:then=`There are less than five books here`:else=`There are 5 books or more here`]]
hide Hides the element if the condition is true [[+numbooks:lt=`1`:hide]]
show Displays an element if a condition is true [[+numbooks:gt=`0`:show]]
then Used to create conditions [[+numbooks:gt=`0`:then=`Books in stock!`]]
else Used to create conditions (together with “then”) [[+numbooks:gt=`0`:then=`Books in stock!`:else=`Sorry, but everything is sold out.`]]
memberof, ismember, mo Checks whether the user is a member of the specified user group [[!+modx.user.id:memberof=`Administrator`]]

Modifiers for working with strings

Modifier Description Usage example
cat Adds a value after the tag [[+numbooks:cat=`books`]]
lcase, lowercase, strtolower Converts all letters to lowercase [[+title:lcase]]
ucase, uppercase, strtoupper Converts all letters to uppercase [[+headline:ucase]]
ucwords Capitalizes the first letter of words [[+title:ucwords]]
ucfirst Capitalizes the first letter in a string [[+name:ucfirst]]
htmlent, htmlentities Converts all characters to corresponding HTML entities [[+email:htmlent]]
esc, escape Safely escapes characters using regular expressions and `str_replace()`. Also escapes MODX tags. [[+email:escape]]
strip Replaces all hyphens, tabs, and any number of spaces with just one space [[+textdocument:strip]]
stripString Cuts the specified substring from a string [[+name:stripString=`Mr.`]]
replace Replaces substrings [[+pagetitle:replace=`Mr.==Mrs.`]]
striptags, stripTags, notags, strip_tags Cuts out all tags (allowed tags can be specified). Do not use for safety purposes. [[+code:strip_tags]]
len,length,strlen Prints the length of a string [[+longstring:strlen]]
reverse, strev Reverses a string character by character [[+mirrortext:reverse]]
wordwrap Inserts a line break after every nth character (words are not broken) [[+bodytext:wordwrap=`80`]]
wordwrapcut Inserts a line break after every nth character, even if this character is inside a word [[+bodytext:wordwrapcut=`80`]]
limit Prints a specified number of characters from the beginning of a line (default is 100) [[+description:limit=`50`]]
ellipsis Adds an ellipsis and truncates the line if it is longer than the specified number of characters (default is 100) [[+description:ellipsis=`50`]]
tag Shielding. Displays the element as it is, without the :tag. For use in documentation [[+showThis:tag]]
add, increment, incr Adds the specified number (default +1) [[+downloads:incr]] [[+blackjack:add=`21`]]
subtract, decrement, decr Subtracts the specified number (default -1) [[+countdown:decr]] [[+moneys:subtract=`100`]]
multiply, mpy Multiplies by the specified number (default *2) [[+trifecta:mpy=`3`]]
divide,div Divides by the specified number (default /2) [[+rating:div=`4`]]
modulus, mod Returns the modulus of a number (default: %2, returns 0 or 1) [[+number:mod]]
ifempty,default,empty,isempty Returns the modifier value if the tag value is empty [[+name:default=`anonymous`]]
notempty, !empty, ifnotempty, isnotempty Returns the modifier value if the tag value Not empty [[+name:notempty=`Hello [[+name]]!`]]
nl2br Replaces newlines \n with the HTML br tag [[+textfile:nl2br]]
date Converts a timestamp to text according to the specified format (date format) [[+birthyear:date=`%Y`]]
strtotime Converts date as text to UNIX timestamp [[+thetime:strtotime]]
fuzzydate Accepts a timestamp and returns a date in the form "Today at 16:20 PM" [[+createdon:fuzzydate]]
ago Returns the number of seconds, minutes, weeks, or months that have passed since the date specified in the tag. [[+createdon:ago]]
md5 Creates an MD5 hash of a value [[+password:md5]]
cdata Wraps the output with CDATA tags [[+content:cdata]]
userinfo Returns the requested value from the user's profile. User ID required [[!+modx.user.id:userinfo=`username`]]
isloggedin Returns 1 if the user is authorized in the current context [[!+modx.user.id:isloggedin:is=`1`:then=`Yes`:else=`No`]]
isnotloggedin Returns 1 if user Not authorized in the current context [[!+modx.user.id:isnotloggedin:is=`1`:then=`No`:else=`Yes`]]
urlencode Converts the value as a URL, that is, uses the PHP `urlencode()` function [[+mystring:urlencode]]
urldecode Converts the value as from a URL, that is, uses the PHP function `urldecode()` [[+myparam:urldecode]]

Modifiers for working with users must be called uncached so that each user sees the latest data.

Using Output Modifiers with Parameters

If a tag has parameters, then they must be written immediately after the modifier:

[[!getResources:default=`Sorry, nothing found`? &tplFirst=`blogTpl` &parents=`2,3,4,8` &tvFilters=`blog_tags==%[[!tag:htmlent]]%` &includeTVs=`1` ]]

Creating a Custom Modifier

Any snippet can be used as an output modifier. To do this, simply specify the snippet name instead of the modifier. For example, let's create a snippet [] that adds a certain number of exclamation points to the output:

[[*pagetitle:makeExciting=`4`]]

Such a tag call will pass the following parameters to the makeExciting snippet for processing:

Parameter Meaning Example value
  1. Overloading class properties
  2. Access to object properties can be overloaded using methods __call, __get And __set. These methods will only fire if the object or inherited object does not contain the property being accessed. The syntax is:

    void __set(string name, mixed value)

    Void __get(mixed name)

    With these methods, class property accesses can be overloaded to execute arbitrary code defined in the class. The name argument contains the name of the property being accessed. The value argument of the __set() method must contain the value that will be assigned to the class property named name.

    __get And __set:

    class Setter(
    public $n ;
    private $x = array("a" => 1 , "b" => 2 , "c" => 3 );

    Function __get ($nm) (
    print "Reading [$nm]\n" ;

    If (isset($this -> x [ $nm ])) (
    $r = $this -> x [ $nm ];
    print "Received: $r\n" ;
    return $r ;
    ) else (
    print "Nothing!\n" ;
    }
    }

    Function __set ($nm, $val) (
    print "Write $val in [$nm]\n" ;

    If (isset($this -> x [ $nm ])) (
    $this -> x [ $nm ] = $val ;
    print "OK!\n" ;
    ) else (
    print "Everything is bad!\n" ;
    }
    }
    }

    $foo = new Setter();
    $foo -> n = 1 ;
    $foo -> a = 100 ;
    $foo -> a++;
    $foo -> z++;
    var_dump($foo);
    ?>

    The result of executing the considered script will be:

    We write 100 in [a]
    OK!
    Read [a]
    Received: 100
    We write 101 in [a]
    OK!
    Read [z]
    Nothing!
    Write 1 in [z]
    Everything is bad!
    object(Setter)#1 (2) (
    ["n"]=>
    int(1)
    ["x:private"]=>
    array(3) (
    ["a"]=>
    int(101)
    ["b"]=>
    int(2)
    ["c"]=>
    int(3)
    }
    }

  3. Method overloading
  4. Method calls can be overloaded using methods __call, __get And __set. These methods will only fire if the object or inherited object does not contain the method being accessed. Syntax:

    mixed __call(string name, array arguments)

    Using this method, class methods can be overloaded to execute arbitrary code defined in the class. The name argument contains the name of the called method. The arguments that were passed to the method when called will be returned via arguments. The value returned by the method __call(), will be passed to the caller.

    Example of overload using __call:

    class Caller(
    private $x = array(1, 2, 3);

    Function __call ($m, $a) (
    print "Method $m:\n called";
    var_dump($a);
    return $this -> x ;
    }
    }

    $foo = new Caller();
    $a = $foo -> test (1 , "2" , 3.4 , true );
    var_dump($a);
    ?>

    The result of running the example considered:

    The test method is called:
    array(4) (
    =>
    int(1)
    =>
    string(1) "2"
    =>
    float(3.4)
    =>
    bool(true)
    }
    array(3) (
    =>
    int(1)
    =>
    int(2)
    =>
    int(3)
    }

  5. Interfaces

    Object interfaces allow the programmer to write code that specifies what methods and properties a class should include, without having to describe their functionality.

    Interfaces are declared in the same way as regular classes, but using the keyword " interface"; method bodies of interfaces must be empty. To include an interface in a class, the programmer must use the keyword " implements" and describe the functionality of the methods listed in the included interface. If required, classes can include more than one interface by listing them separated by a space.

    If a class includes an interface and does not describe the functionality of all methods of that interface, executing code using that class will fail with a fatal error indicating which methods were not described. Interface example:

    interface ITemplate
    {
    public function setVariable ($name, $var);
    public function getHtml ($template );
    }

    Class Template implements ITemplate
    {
    private $vars = array();

    Public function setVariable ($name, $var)
    {
    $this -> vars [ $name ] = $var ;
    }

    Public function getHtml ($template )
    {
    foreach($this -> vars as $name => $value ) (
    $template = str_replace ("(" . $name . ")" , $value , $template );
    }

    Return $template ;
    }
    }
    ?>

  6. instanceof operator

    Support for checking dependencies on other objects. The is_a() function, known from PHP 4, is no longer recommended.

    if ($obj instance of Circle) (
    print "$obj is a Circle" ;
    }
    ?>

  7. final method

    The final keyword allows you to mark methods so that an inheriting class cannot overload them. By placing a keyword before declarations of methods or properties of a class final, you can prevent them from being overridden in child classes, for example:

    class BaseClass(
    public function test() (
    echo "Method BaseClass::test() called\n";
    }

    Final public function moreTesting() (
    echo "BaseClass::moreTesting() method called\n";
    }
    }

    Class ChildClass extends BaseClass (
    public function moreTesting() (
    echo "ChildClass::moreTesting() method called\n"<<< Назад

    Content Forward >>>
    If you have any other questions or something is not clear - welcome to our