If you have such a complex array with restrictions for each individual member, I would not use an anonymous array, but rather a well-defined object. With an array, you can never be sure that it holds, which is somewhat reminiscent of the transmission of an “Object”, for example. Java, which you rarely consider a good choice.
However, there is the possibility of a small hint when the array contains objects of a certain type, as described here , but this is not a very good answer to your question.
If you really need a parameter as an array, you can document it as you suggested in the method description; however, if you use the object as a parameter, you will have additional support in modern IDEs (IntelliSense, etc.).
EDIT: I mean, for me the question will be “why do I want to use an anonymous array instead of a specific type” - and, in addition, simplicity (which will have unpleasant consequences as a technical debt later if you maintain and extend your code), I I can’t think of a reason, especially compared to what you get when using a user-defined type (self-documenting code, restrictions visible and made explicit by standard methods, etc.).
If you just need a data dump, you can go with a simple array, but since you are already thinking about additional and necessary keys, this screams for a user-defined type.
EDIT2: Regarding your comment about whether you already have an array as a source: I'm not sure whether to pass it as an array or perform “match” operations as soon as you get the array (like $ _POST or as return value from any third-party library or internal PHP functions or such).
I suppose it can be argued that this is not a model business for interpreting data generated by representations (for example, HTML forms, POST data), but rather controller compatibility in order to respond accordingly to the input and transmit the model in the appropriate state. I mean, you can do something like this if you get, for example, an array like $ _POST:
$customer = new Customer(); $customer->setId($_POST['id']); $customer->setName($_POST['name']); $customer->setTown($_POST['town']);
And handle the errors as soon as you gain access to $ customer, for example. throwing exception if name is not set (i.e. $_POST['name'] was empty or that). Thus, you use the original array to call the setters of the object, and not, for example. passing the array to the factory as Customer::buildByHttpPostData(array $data) and thereby delegating knowledge of the presentation information (names of HTML input tags, etc.).
There is a “standard” way on the bottom line to declare the required or optional array keys, and of course you can describe these restrictions in the method description, but maybe you can get around this by supporting supported methods such as PHPDoc comments on setters or getters.
Of course, there may be better ways to get closer to the problem, and maybe someone will come up with a better answer on how to handle this.