Skip to content

Add #[NamedParameterAlias] attribute #6522

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from

Conversation

beberlei
Copy link
Contributor

@beberlei beberlei commented Dec 19, 2020

The sooner we add this functionality to allow a second alias name of a parameter when using the new "named parameters" feature, the better, because open source libraries will only be able to use this across versions when their minimum version includes this feature.

In the long term, open source code (closed source as well) will want the ability to a.) build APIs using named params b.) have the flexibility to refactor parameter names.

Addressing this need with an attribute would in my opinion be a good idea, because it requires very little code and only affects the code path of named params in a single isolated place.

RFC: https://wiki.php.net/rfc/named_parameter_alias_attribute

@beberlei beberlei marked this pull request as draft December 19, 2020 11:02
@cmb69 cmb69 added the RFC label Dec 19, 2020
Copy link
Contributor

@TysonAndre TysonAndre left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like useful functionality to have; the additional error handling and handling of edge cases should be possible to add.

void validate_namedparameteralias_attribute(zend_attribute *attr, uint32_t target, zend_class_entry *scope)
{
if (attr->argc != 1) {
zend_error_noreturn(E_ERROR, "NameAlias::__construct(): Argument #1 ($alias) is required");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
zend_error_noreturn(E_ERROR, "NameAlias::__construct(): Argument #1 ($alias) is required");
zend_error_noreturn(E_ERROR, "NamedParameterAlias::__construct(): Argument #1 ($alias) is required");

--FILE--
<?php

function test(#[NamedParameterAlias("bar")] $foo) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There should be more tests and checks in the implementation, e.g.

  1. Should invalid identifiers such as NamedParameterAlias("some-name") throw or be silently forbidden for invalid variadic arguments?
  2. Should php warn/throw if using an alias for a parameter name that was already used? (test(#[NamedParameterAlias("bar")] $foo = null, $bar = null))
  3. Handling supporting more than one alias?

i
);

if (attribute && zend_string_equals(arg_name, Z_STR(attribute->args[0].value) )) {
Copy link
Contributor

@TysonAndre TysonAndre Dec 19, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A complete implementation of this would support

  1. Supporting or forbidding multiple NamedParameterAlias values on a single parameter (EDIT: The RFC mentions that's still undecided, but my preference would be repeatable)
  2. Error checking - (1) check if there is at least one argument, (2) check if the type is a string, (3) possibly attempt to evaluate the argument if the type is IS_CONSTANT_AST and check for EG(exception) and return -1
  3. It may be useful to add some sort of bit flag to the function when compiling to indicate that there are parameter aliases to avoid the need to loop in cases EXPECTED(*cache_slot == fbc) doesn't always return early for, such as dynamic calls or polymorphism.

Also, it may be more efficient for the common case to loop over all arguments first and check arg_info->name, and only try to load the attributes in a separate loop if the first attempt failed (efficient for non-variadic functions, but not for variadic functions)

@@ -4354,6 +4355,21 @@ static zend_always_inline uint32_t zend_get_arg_offset_by_name(
*(uintptr_t *)(cache_slot + 1) = i;
return i;
}

if (fbc->common.attributes != NULL) {
zend_attribute *attribute = zend_get_parameter_attribute_str(
Copy link
Contributor

@TysonAndre TysonAndre Dec 19, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like it'd be a bit more efficient to do this outside of this loop and find the parameters where attr->offset > 0 to avoid quadratic runtime - e.g. if there was a function with 20 parameters with attributes, it'd loop over all 20 of them just to find the ones at position i, performing 400 operations

The index to use would be attr->offset - 1

static zend_attribute *get_attribute_str(HashTable *attributes, const char *str, size_t len, uint32_t offset)
{
	if (attributes) {
		zend_attribute *attr;

		ZEND_HASH_FOREACH_PTR(attributes, attr) {
			if (attr->offset == offset && ZSTR_LEN(attr->lcname) == len) {
				if (0 == memcmp(ZSTR_VAL(attr->lcname), str, len)) {
					return attr;
				}
			}
		} ZEND_HASH_FOREACH_END();
	}

	return NULL;
}

ZEND_API zend_attribute *zend_get_parameter_attribute(HashTable *attributes, zend_string *lcname, uint32_t offset)
{
	return get_attribute(attributes, lcname, offset + 1);
}

@tuqqu
Copy link
Contributor

tuqqu commented Dec 20, 2020

what do you think of renaming it to just #[ParamAlias]?

@iluuu1994
Copy link
Member

@beberlei Are you still planning on pursuing this RFC?

@beberlei
Copy link
Contributor Author

@iluuu1994 realistically i do not have time at the moment, do you want to take over?

@iluuu1994
Copy link
Member

Not personally. I'm closing stale PRs. And since named params have been introduced we've had little feedback about needing this feature so I'm not sure it's even necessary.

Can this PR and RFC be closed?

@beberlei beberlei closed this Oct 1, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants