Как использовать оператор use и функцию целочисленного деления в php 7





Appendix

SPL Data Structures

An infamous example is that meaning inherits the and methods from .

Therefore, if a user decides to use these methods instead of the designated and methods the behaviour obtained is the one of a stack instead of a queue.

Reflection

Currently within the Reflection extension we have the following classes and however their purpose isn’t exactly to present a type.

From PHP’s type system perspective, a class is a type. Therefore, not extending from could be seen as questionable. Thus the class acts more as a type constraint and renaming it to may be a good way to clarify it’s concern.

Thus, in a revamped Reflection extension one could imagine a more accurate to represent the current and introduce a new top reflector for all types current, and future. E.g Enums, Generics, etc.

Examples

These examples cover some simple operations and show how the short-hand syntax is easier to read compared to the existing long-hand syntax.

Array sort with user function

Sort which contains objects which have a property named .

Current syntax:

usort($array, 
	function($a, $b) {
		return $a->val <=> $b->val; 
	}
);

New syntax:

usort($array, ($a, $b) ~> $a->val <=> $b->val);

Extracting data from an array and summing it

Current syntax:

function sumEventScores($events, $scores) {
    $types = array_map(
        function($event) {
            return $event'type';
        },
        $events
    );
 
    return array_reduce(
        $types,
        function($sum, $type) use ($scores) {
            return $sum + $scores$type;
        }
    );
}

New syntax:

function sumEventScores($events, $scores) {
    $types = array_map($event ~> $event'type', $events);
    return array_reduce($types, ($sum, $type) ~> $sum + $scores$type);
}

The calling code for this function would be:

$events = array(
    array(
        'type' =>'CreateEvent',
        'date' => '2015-05-01T16:19:33+00:00'
    ),
    array(
        'type' =>'PushEvent',
        'date' => '2015-05-01T16:19:54+00:00'
    ),
    //...
);
 
$scores = 
    'PushEvent'          => 5,
    'CreateEvent'        => 4,
    'IssuesEvent'        => 3,
    'CommitCommentEvent' => 2
;
 
sumEventScores($events, $scores);

Lazy evaluation

It may be necessary to have code only evaluated under specific conditions, like debugging code:

function runDebug(callable $func) {
    /* only run under debug situations, but don't let it interrupt program flow, just log it */
    if (DEBUG) {
        try {
            $func();
        } catch (Exception $e) { /*... */ }
    }
}
 
$myFile = "/etc/passwd";
 
/* Old code */
runDebug(function() use ($myFile) { /* yeah, we have to use use ($myFile) here, which isn't really helpful in this context */ 
    if (!file_exists($myFile)) {
        throw new Exception("File $myFile does not exist...");
    }
});
 
/* New code */
runDebug(() ~> {
    if (!file_exists($myFile)) {
        throw new Exception("File $myFile does not exist...");
    }
});
 
/* still continue here, unlike an assert which would unwind the stack frame here ... */

Partial application

The shorthand syntax makes it easier to write functional code like a reducer by using the ability of shorthand anonymous functions to be chained together easily.

Current syntax:

function reduce(callable $fn) {
    return function($initial) use ($fn) {
        return function($input) use ($fn, $initial) {
            $accumulator = $initial;
            foreach ($input as $value) {
                $accumulator = $fn($accumulator, $value);
            }
            return $accumulator;
        };
    };
}

New syntax:

function reduce(callable $fn) {
    return $initial ~> $input ~> {
        $accumulator = $initial;
        foreach ($input as $value) {
            $accumulator = $fn($accumulator, $value);
        }
        return $accumulator;
    };
}

Future Scope

Optimization of the directory structure of to reflect the namespace structure. This could help to avoid those 1,000+ LOC files, and would aid discoverability as well as mapping of a user-level symbol to its actual position in the repository. An optimized directory structure could look as follows:

/
├── bin/
│   ├── appveyor/...
│   ├── travis/...
│   ├── php
│   ├── php-cgi
│   ├── php-dbg
│   ├── php-ext
│   ├── php-fmt
│   ├── php-fpm
│   ├── php-ize
│   ├── php-pkg
│   ├── php-tst
│   └── ...
├── bld/
│   ├── debug/
│   │   ├── x64/...
│   │   └── x86/...
│   ├── debug-ts/
│   │   ├── x64/...
│   │   └── x86/...
│   ├── release/
│   │   ├── x64/...
│   │   └── x86/...
│   └── release-ts/
│       ├── x64/...
│       └── x86/...
├── doc/
│   ├── building/
│   │   ├── supported-platforms.ad
│   │   ├── unix.ad
│   │   ├── windows.ad
│   │   └── ...
│   ├── guidelines/
│   │   ├── coding-standard-c.ad
│   │   ├── coding-standard-php.ad
│   │   ├── git-rules.ad
│   │   ├── mailing-list-rules.ad
│   │   └── ...
│   ├── stubs/...
│   ├── testing/...
│   └── ...
├── etc/
│   ├── php/
│   │   ├── development.ini
│   │   ├── logo.svg
│   │   └── production.ini
│   └── ...
├── lib/...
├── src/
│   ├── mysql/...
│   ├── php/
│   │   ├── engine/...
│   │   ├── locale/...
│   │   ├── logging/...
│   │   ├── reflection/...
│   │   └── ...
│   ├── postgresql/...
│   ├── sapi/
│   │   ├── apache/...
│   │   ├── cgi/...
│   │   ├── cli/...
│   │   ├── fpm/...
│   │   └── ...
│   ├── win32/...
│   ├── zend/...
│   └── ...
├── tst/...
├── var/...
├── README.ad
└── ...

Proposal

A new method is added to , with the following signature:

mixed Closure::call(object $to, mixed ...$parameters)

It calls the closure with the given parameters and returns the result, with bound to the given object , using the scope of the given object. Like the () methods, a static class cannot be bound (using will fail).

It can be used like so:

$foo = new StdClass;
$foo->bar = 3;
$foobar = function ($qux) { var_dump($this->bar + $qux); };
$foobar->call($foo, 4); // prints int(7)

The method, unlike (), does not take a scope parameter. Instead, it will always use the class of the object as its scope. Thus:

class Foo { private $x = 3; }
$foo = new Foo;
$foobar = function () { var_dump($this->x); };
$foobar->call($foo); // prints int(3)

Introduction

The purpose of this RFC is to document issues regarding the current namespace implementation (and where appropriate and available, to link to available patches), whether percieved or otherwise. Additionally it aims to offer a reference to mailing list conversations past and present regarding namespaces as well as link to a number of third party articles/blog-entries that discuss namespaces (for those that might be interested in finding out a little more about how namespaces are being recieved in the wild.)

Common Misconceptions

This document does not serve to force any particular changes in the engine regarding namespaces, as such any or all of the issues defined may at some stage be declared resolved without any implementation changes; It should accepted that some issues may only exist in so far that users have misinterpreted the current namespace functionality.

It is not the intention of this document to prove namespaces are either useless or counter-productive. On the contrary it is the aim of this document to aid the resolution of a number issues (percieved or otherwise) in the hope that this will result in the best possible implementation prior to it’s first official release, whereby a positive addition to the language will be introduced that is also perceived to be as such. With that in mind, thanks are extended to the developers who are responsible for the namespace implementation thus far … without them we would have nothing!

Introduce -> as Classname->Method() syntax

At first look, this appears to be an option, but as I explain below, the ambiguity between static class elements and namespaced elements is deeply embedded in the current implementation, and is not possible to resolve, thus disqualifying any solution that preserves :: as separator. However, disregarding those reasons, here are the ways to consider this option:

This could be done in a couple of ways:

The first way (which was Stas’s proposal):

allow -> as an alternate syntax

The second way (which was Lukas’s understanding of Stas’s proposal):

  • move all static method calls to -> syntax
  • introduce E_STRICT (or E_DEPRECATED) for :: calls that resolve to static method in PHP 6, but allow both syntaxes without error in PHP 5.3

The second way is going to be a terrible headache for all OO authors, past and present, and will prevent migration to PHP 5.3 for those who are only looking for security fixes and performance improvements. As such, it can be safely vetoed as impractical. The next section assumes we are talking about Stas’s original proposal, which is to allow this syntax:

<?php
class foo {
    static function bar(){echo "bar\n";
}
foo::bar(); // "bar"
foo->bar(); // "bar"

and this syntax in case of name conflict:

<?php
namespace foo::foo;
function bar(){echo "bar function\n";}
namespace foo;
class foo {
    static function bar(){echo "bar\n";
}
::foo::foo::bar(); // "bar function"
::foo::foo->bar(); // "bar"

The same is true for constants:

<?php
namespace foo::foo;
const BAR = 2;
namespace foo;
class foo {
    const BAR = 1;
}
echo ::foo::foo::BAR; // 2
echo ::foo::foo->BAR; // 1

Problems

  • if classname::method() is used, the ambiguity problem still exists
  • all existing code uses classname::method() or classname::CONST. Thus, to be protected from the ambiguity, all existing OO code would need to

be modified.

  • The syntax $blah::hello() is introduced in PHP 5.3, and is equivalent to blah::hello() if $blah = ‘blah’; blah::hello() is equivalent to blah->hello(), but $blah::hello() is not equivalent to $blah->hello(), and the same is true of constants — it introduces another logical failure path for new users
  • it redefines a long-established definition of ->, which is not necessarily a problem, but could be confusing for both existing and new users
  • only educated users would ever think to look for -> unless a massive PR campaign is mounted, which would also make PHP look disorganized. This is simply a political and NOT a technical problem.
  • does not solve the deeper problems inherent in ::

Use \ as namespace separator

Before discussing the advantages and problems, it is prudent to look at some of the intrinsic problems in the implementation of namespaces with :: as the separator before continuing.

With current CVS of PHP_5_3, these two code samples work very differently:

file1.php:

<?php
namespace foo;
function bar(){echo "func\n";}

main.php:

include 'file1.php';
class foo {
static function bar(){echo "method\n";}
}
foo::bar(); // func
?>
<?php
namespace foo::foo;
function bar(){echo "func\n";}
namespace foo;
class foo {
static function bar(){echo "method\n";}
}
foo::bar(); // method
?>

The actual line of code “foo::bar()” does not change, but the addition of a namespace to the file changes the name resolution. In global code, “foo::bar()” always refers to namespaced function “foo::bar.” Inside of a namespace declaration, “foo::bar()” always refers to a class method.

Although this may appear on the surface to simply be a name resolution bug, the problem is much deeper. PHP performs resolution at run-time for namespaced functions and for class methods. Basically, the opcode will check for two different scenarios:

1) in namespaced code:

  • check for “foo::bar” function
  • check for “foo::foo” class

2) in global code:

check for “foo::bar” function

Note that inside the namespaced code, there is no attempt to check for ::foo::bar (foo::foo::bar). In order to implement this, we would need to have potentially 4 hash lookups for every static method call:

  • check for function nsname::
  • check for function
  • check for class nsname::
  • check for class

In this list, is the way the method/function is called, so in the code sample above, it would be “foo::bar”, and we would check for:

  • check for function foo::foo::bar
  • check for function foo::bar
  • check for class foo::foo::bar
  • check for class foo::bar

This would potentially double the autoload lookups for classes, and thus is actually impossible to solve correctly with the current implementation.

The only solution is to re-define how name lookup works, such that it is different for straight “blah()” or “new blah” vs. “that::way()” or “new that::way.” However, because we use :: for the namespace separator, it is literally impossible to decide whether “that::way()” should be treated as our unqualified “way” or as qualified “that::way.” The only way to solve this problem is to use a different namespace separator.

inefficiency due to ambiguity

The current implementation of namespaced functions and static methods requires code execution to go through the same location. The same is true of class constants vs. namespaced constants. For every class static method and class static constant, there is at least 1 extra hash table lookup, regardless of whether any namespaced classes/constants are defined.

As a side note, PHP 5.3 is so much faster than PHP 5.2, this is not a worthwhile comparison, and is not the best base to compare against, so I will be instead comparing PHP 5.3 CVS vs. a patched PHP 5.3 with a different namespace separator.

If another namespace separator is used, every static method call has 1 fewer hash table lookup, and every constant resolution saves potentially 2 hash table lookups (internal details can be explained if you’d like — nothing is more complicated under the hood than the way constants are resolved, so I will leave those out of this RFC). In addition, it is possible to perform much more efficient parsing of namespaced function calls than it is under the current implementation, because zend_do_begin_function_call() can be used instead of zend_do_begin_class_member_function_call(), which is much more efficient due to checks in class_member_function_call that are unnecessary for namespaced functions, but absolutely necessary if the namespace separator is ::.

Code review gotchas

Neither human review nor automated tools can look at:

<?php Blah::blah::blah(); ?>

and successfully resolve what it is in fact doing. This will hamper automated security auditing tools, code correctness tools, and human review of patches, creating a situation that makes debugging and designing robust PHP code harder than it is now. This can only be corrected via a new namespace separator.

RFC Impact

On Functional Programming / Autoloading

Group use declarations is also very helpful for the PHP user base more fond to the functional programming paradigms. This RFC would be especially important if PHP finally gets function autoloading.

// Current use syntax:
 
use function iter\range;
use function iter\map;
use function iter\filter;
use function iter\apply;
use function iter\reduce;
use function iter\fn\operator;
 
// Proposed syntax:
 
use function iter\{ range, map, filter, apply, reduce, fn\operator };

On Backward Compatibility

This RFC is backwards compatible with previous PHP releases. It’s worth to add that the patch will not affect runtime performance.

Proposal

This RFC proposes the definition of a standardized namespace structure for PHP internals by utilizing the reserved PHP namespace as the root for all code that is supplied by the PHP Group, and vendor specific root namespaces for symbols that are contributed to the PHP Group from other vendors.

Coding Standard

An important question to answer, if namespaces are to be used by PHP, is how they should be written. PHP has a long history of inconsistency, it therefore makes sense to define this upfront to ensure that any future effort is not going to introduce new inconsistencies.

There are two possible choices:

  1. snake_case
  2. PascalCase (in accordance with class naming)

Arguments for both approaches exist, however, it is after all a purely cosmetic question:

Another question in this regard is whether to allow plural forms (e.g. collection vs collections) in namespaces or not.

Structure

User-level code provided directly by the PHP Group goes into the PHP vendor namespace. The vendor namespace itself does not contain any code directly, but instead is split into multiple sub-namespaces. This should ensure that we are not creating a new global namespace where everything that cannot be categorized ends up in.

The sub-namespaces are basically endless, anything that makes sense can go there. It is up to the PHP Group to decide on them. Examples for namespaces are:

  • Core or lang — contains everything that is directly bound to the PHP language itself, and which cannot be removed without breaking the language itself. Examples include primitive types that are directly understood by the parser (e.g. arrays, integers, strings), as well as other basic types and functionality that is directly coupled to the parser (e.g. , , , ).
  • Crypto — could contain cryptographic abstractions, without exposing the underlying library and technology.
  • IO — could contain a file system abstraction, with a possible sub-namespace for streams.
  • Locale — could contain abstractions to work with different languages without exposing the underlying library or technology.
  • Logging — could contain an abstraction for logging that is extensible by user-level software.
  • OPCache — should be self-explanatory.
  • Parser — could expose the PHP parser to user-level software for their own usage. This could contain further sub-namespaces for tokens, AST, etc.
  • PHAR — should be self-explanatory.
  • Regex — could contain abstractions for regular expressions, without exposing the underlying library or technology.
  • Reflection — should be self-explanatory.
  • Std or util — for tiny self-contained types (e.g. ) and functionality where it would be total overkill to create a dedicated namespace for (e.g. UUID). Care must be taken that not everything ends up in this namespace. Basically, anything that requires more than an interface-class-exception combination should not go into this namespace. The situation with single functions is even more complicated. These decisions must be made on a case-by-case basis by the PHP Group. Defining strict rules seems counterproductive.

Extensions which are provided by third-party vendors to the PHP Group for future maintenance and inclusion in the PHP repository go into their own vendor namespace. This should ensure that we can move those extensions to PECL without breaking compatibility on a namespace level. It also clearly communicates that that code is not from the PHP Group directly.

Vendor names must not be random, to ensure that they cannot collide with the names that users choose for their own namespaces. Names of companies or organizations (e.g. MySQL, Oracle, W3C) are always a good choice, because they are protected trademarks and nobody is allowed to actually impersonate them. Widely known software names across programming languages (e.g. curl, PCRE, JSON) are also good choices, as the likelihood of someone using them for their own namespaces is low.

That being said, the decision whether a name is a good root namespace for a vendor or not must be decided on a case-by-case basis by the PHP Group. A search on Packagist for existing users should be the minimum requirement there.

What Goes Where?

This question is actually one that the PHP Group had to ask themselves since modules were introduced to internals. The choice is basically simple, group related things by use-case, reuse, and conceptual cohesion. Hence, this is always something that needs to be decided on a case-by-case basis. The introduction of new namespaces should not be problematic, as long as it is clear that that namespace will contain more than just two classes.

Introduction

The PHP project has reserved the right to use the namespace but has never acted upon starting to use it. We therefore propose that core symbols which cannot be unbundled such as those related to the language/parser/interpreter must use namespace.

This paves the way to reduce the usage of the global namespace as “PHP”’s namespace.

This is not a concrete proposal on how to structure the namespace or a proposal on re-aliasing classes to use the namespace. This is only an agreement that core classes or newly introduced symbols which are tightly coupled to the PHP engine must start using the namespace, e.g. for type prefixes.

Introduction

The amount of pain and grief all interested developers have undergone to perfect namespaces has got to end. This RFC provides a close examination of the four potentially viable solutions and the major problems and advantages of each.

First, ways of solving the conflict between namespaced elements and static class elements:

  • remove functions/constants from namespaces
  • conflict resolution via “use namespace” or other similar proposals
  • introduce -> as Classname->Method() syntax
  • use \ as namespace separator

This RFC assumes that since we are in agreement about how to resolve class names, that it is not in need of further discussion. To be clear, the agreed upon resolution order is:

  • check nsname::classname
  • autoload nsname::classname if necessary
  • fail

which requires all internal classes to be ::prefixed (or \prefixed if the separator is changed to \).

Conflict resolution via «use namespace» or other similar proposals

Unfortunately, this is not an option. Here is the background:

This is actually only implementable in one way:

  • Define a clear resolution order in favor of classes or in favor of namespaced elements
  • Require the others to explicitly define an element as a class or as a namespace:

If we favor classes:

<?php
// always calls class Blah::blah, method thing()
Blah::blah::thing();
?>

and

<?php
use namespace Blah::blah;
// always calls function Blah::blah::thing()
blah::thing();
?>

If we favor namespaces:

<?php
// always calls function Blah::blah::thing()
Blah::blah::thing();
?>

and

<?php
use class Blah::blah;
// always calls class Blah::blah, method thing()
blah::thing();
?>

Advantages

  • conflicts can be controlled explicitly via “use namespace” or “use class”
  • resolution is clear if access to the whole file is available

Problems

  • unqualified name (Blah::blah::thing or ::Blah::blah::thing) is unavailable for elements accessed via “use namespace/class” syntax, short name must always be used
  • Code review (checking patches for errors) becomes impossible — one must always have full access to the entire file. This is necessary at places like Google, where separate managers review commits of underlings and “import/use” is forbidden.

In my opinion, the 2nd problem renders this solution and any like it completely unusable. We need a solution that allows ::fully::delimited::names.

Future Scope

Other uses for ~> operator

This RFC is solely for using the shorthand anonymous functions as closures. It does not cover any other usage of the shorthand function definition such as:

class Foo {
    private $bar
 
    getBar() ~> $this->bar;
    setBar($bar) ~> $this->bar = $bar;
}

Which is outside the scope of this RFC.

Type Hints and Return Types

This RFC does not include type hints nor return types.

Type Hints are not added due to technical problems in parser and the RFC author is not sure about whether they should be really added. If anyone achieves to solve these technical issues, he should feel free to do that in a future RFC for further discussion. And as introducing half a typesystem would be inconsistent, the RFC proposes to not include return types either.

As an alternative, the current syntax for defining Closures still can be used here.

Description of what it means to use \ as namespace separator

The main issues that using \ as namespace separator can address are:

  • name ambiguity between static class methods/constants and namespace functions/constants
  • name resolution order differences and gotchas of foo::bar() in a namespace vs. in global code
  • code review problems

Advantages

  • name ambiguity is impossible
  • \ is visually quite different from ::, and is easier to scan and detect as namespace instead of static class operator
  • \ is a single keystroke on U.S. keyboard layout without shift key
  • \this\is\used for paths on Windows and is intuitively familiar to those developers. According to a php|arch survey (as relayed by Steph Fox), most of their readers develop on Windows and deploy on Unix, which would imply that \these\paths are familiar
  • \this\maps\to\filesystem layouts often used by autoload intuitively for the above reason
  • because \ is a single keystroke, it is possible to require \ prefix for all global functions/classes/constants, and conversion is minimally effortful (from personal experience trying to convert files to several namespace separators to see what it was like)
  • code review ambiguities disappear permanently
  • name resolution order problems disappear if we decide that “foo\bar” or “foo::bar” is always prefixed with namespace name, and only short names like “bar()” or “new bar” are checked for both “nsname\bar()” and internal function “bar()” or “class nsname\bar” and internal class “bar”. “\foo\bar” is used for global scoping.
  • code coverage of namespace-related code is so good, it is possible to be very confident of the correctness of the patch.

Problems

  • \ looks a lot like / and is easy to accidentally flip, especially for unix users
  • \ is used for escaping
  • inside a string a namespace name becomes\\like\\this or we can get weird characters. This could be confusing to users at first.
  • all existing namespaced code must be nearly rewritten, a simple search/replace of :: would not be possible.
  • the patch touches a lot of the engine, and will need rigorous battle-testing.
  • to many, \this\way will look weird at first.

Patch

It is worth noting that 95% of this patch took me 2 hours to write, the constant resolution code took me an entire week, and the unit test modifications took only 1-2 hours, so porting this to HEAD should be pretty simple, but the initial work was non-trivial and I hope that the final solution could be based off of this work.


С этим читают