How to Create a PHP Function That I Can Call Without Parentheses

Can I create a PHP function that I can call without parentheses?

print is not a variable functions

Because this is a language construct
and not a function, it cannot be
called using variable functions

And :

Variable functions

PHP supports the concept of variable
functions. This means that if a
variable name has parentheses appended
to it, PHP will look for a function
with the same name as whatever the
variable evaluates to, and will
attempt to execute it. Among other
things, this can be used to implement
callbacks, function tables, and so
forth.

Creating functions without parentheses in PHP like 'echo'

There simply isn't. "echo" is more of an operator than a function, so you'd actually need to rewrite the PHP interpreter source in order to introduce new "functions" like those.

Edit: Actually, the more accurate term for "echo" is, as eyze has correctly pointed out, language construct rather than operator. http://php.net/manual/de/function.echo.php provides some more information.

php function calls with and without brackets

It is done by implementing the __get magic method:

class User {
private $posts = [1, 2, 3];

public function __get($key) {
if ($key === 'posts')
return $this->$key;
}

public function posts() {
return count($this->posts);
}
}

$u = new User;
var_dump($u->posts());
var_dump($u->posts);

Output

int(3)
array(3) {
[0]=>
int(1)
[1]=>
int(2)
[2]=>
int(3)
}

Custom PHP function passing arguments without brackets

In the PHP language specifications, the Function calls section mentions:

A function is called via the function-call operator ().

The syntax of a Function Call Expression is:

function-call-expression:

    qualified-name ( argument-expression-list opt )

    callable-expression ( argument-expression-list opt )

As you can see, a function call expression is made from function name (either the name as a token or a PHP expression that evaluates to it) followed by open parenthesis ((), an optional list of arguments (separated by comma (,)), followed by the close parenthesis ()).

The parentheses are required and they allow the compiler tell apart a constant from a call of a function without arguments. They also allow it to understand that $var() is a function call while $var is not.


Have anyone managed to create a custom PHP function where by passing arguments / parameter wihtout braces, similar to something like echo?

The short answer is "No, this is not possible in PHP". Above is explained why not.


echo is not a function, it is a language construct. The same for print, include/include_once/require/require_once.

Using echo with parenthesis is possible but, in fact, the parenthesis are not part of the echo, they are part of the expression that is printed by echo.

The following code is invalid:

echo('one', ' ', 'two');

It would be valid if echo were a function.

Look at this code:

echo('one'), ' ', ('two');

This is apparently invalid PHP error code. And it would be invalid if echo were a function. But echo is not a function and the code is valid.

Let's assume, for a moment, that echo is a function and the language allows calling functions with or without parentheses. The line above is ambiguous. What are the arguments of echo?

Should the compiler think the call to echo is echo('one') or ('one') is just a string in parentheses (a perfectly valid expression) and the arguments of echo are ('one'), ' ' and ('two')?

Instantiate a class with or without parentheses?

Any difference in performance is going to be absolutely negligible.

While both ways are fine, I personally would prefer using new Car(); because usually, a method is being called here, and function/method calls in PHP require (). Also, it's more consistent with instantiations that have parameters.

But in the end, it's down to taste. It doesn't matter which way you choose, but when you choose one, stick to it consistently!

laravel - why function call with no parentheses?

Laravel uses the magic function __get to handle arbitrary attributes.

This calls Illuminate\Database\Eloquent\Model's getAttribute function, which checks the model's relations and returns the related item(s) if a relationship is present with that name.

The parentheses are not needed because getAttribute automatically executes the function items() when the attribute items is requested. You can, by the way, request Auth::user()->item(); which will return a query builder you can work with.

Implement method calling without parentheses

Your problem is a parsing problem. However, the bigger problem is that your language is not syntax-directed. This means that the syntax of your language doesn't match its semantics. For example, consider the following program in your language:

f g

This can be parsed in one of two ways, f applied to g or g applied to f. However, the syntax of the language doesn't make it apparent which one of the two parse trees will be generated. As EJP correctly mentioned, "it is more than a parsing problem, it is a semantic-feedback problem".

So, how to you make your language syntax-directed? Let's take a cue from object-oriented languages. For example:

1 plus 2

In an object-oriented language like JavaScript this might be written as:

Number.prototype.plus = function (n) {    return this + n;};
var sum = (1) .plus (2);
alert(sum);

Invoking a function without parentheses

There are several different ways to call a function without parentheses.

Let's assume you have this function defined:

function greet() {
console.log('hello');
}

Then here follow some ways to call greet without parentheses:

1. As Constructor

With new you can invoke a function without parentheses:

new greet; // parentheses are optional in this construct.

From MDN on the new oprator:

Syntax

new constructor[([arguments])]

2. As toString or valueOf Implementation

toString and valueOf are special methods: they get called implicitly when a conversion is necessary:

var obj = {
toString: function() {
return 'hello';
}
}

'' + obj; // concatenation forces cast to string and call to toString.

You could (ab)use this pattern to call greet without parentheses:

'' + { toString: greet };

Or with valueOf:

+{ valueOf: greet };

valueOf and toString are in fact called from the @@toPrimitive method (since ES6), and so you can also implement that method:

+{ [Symbol.toPrimitive]: greet }
"" + { [Symbol.toPrimitive]: greet }

2.b Overriding valueOf in Function Prototype

You could take the previous idea to override the valueOf method on the Function prototype:

Function.prototype.valueOf = function() {
this.call(this);
// Optional improvement: avoid `NaN` issues when used in expressions.
return 0;
};

Once you have done that, you can write:

+greet;

And although there are parentheses involved down the line, the actual triggering invocation has no parentheses. See more about this in the blog "Calling methods in JavaScript, without really calling them"

3. As Generator

You could define a generator function (with *), which returns an iterator. You can call it using the spread syntax or with the for...of syntax.

First we need a generator variant of the original greet function:

function* greet_gen() {
console.log('hello');
}

And then we call it without parentheses by defining the @@iterator method:

[...{ [Symbol.iterator]: greet_gen }];

Normally generators would have a yield keyword somewhere, but it is not needed for the function to get called.

The last statement invokes the function, but that could also be done with destructuring:

[,] = { [Symbol.iterator]: greet_gen };

or a for ... of construct, but it has parentheses of its own:

for ({} of { [Symbol.iterator]: greet_gen });

Note that you can do the above with the original greet function as well, but it will trigger an exception in the process, after greet has been executed (tested on FF and Chrome). You could manage the exception with a try...catch block.

4. As Getter

@jehna1 has a full answer on this, so give him credit. Here is a way to call a function parentheses-less on the global scope, avoiding the deprecated __defineGetter__ method. It uses Object.defineProperty instead.

We need to create a variant of the original greet function for this:

Object.defineProperty(window, 'greet_get', { get: greet });

And then:

greet_get;

Replace window with whatever your global object is.

You could call the original greet function without leaving a trace on the global object like this:

Object.defineProperty({}, 'greet', { get: greet }).greet;

But one could argue we do have parentheses here (although they are not involved in the actual invocation).

5. As Tag Function

Since ES6 you can call a function passing it a template literal with this syntax:

greet``;

See "Tagged Template Literals".

6. As Proxy Handler

Since ES6, you can define a proxy:

var proxy = new Proxy({}, { get: greet } );

And then reading any property value will invoke greet:

proxy._; // even if property not defined, it still triggers greet

There are many variations of this. One more example:

var proxy = new Proxy({}, { has: greet } );

1 in proxy; // triggers greet

7. As instance checker

The instanceof operator executes the @@hasInstance method on the second operand, when defined:

1 instanceof { [Symbol.hasInstance]: greet } // triggers greet


Related Topics



Leave a reply



Submit