phpgt / input Goto Github PK
View Code? Open in Web Editor NEWEncapsulated and type-safe user input.
Home Page: https://www.php.gt/input
License: MIT License
Encapsulated and type-safe user input.
Home Page: https://www.php.gt/input
License: MIT License
More than one file can be uploaded in the same field. How should this be handled?
A single file is represented by a FileUpload
currently. Maybe a MultiFileUpload
object is the simple solution here.
Even though XSS isn't really a threat when using the DOM, most user input probably wants to be put through something like strip_tags. Someone's username of <script>alert("pwned")</script>
would be nice to clean up.
Register a callback function to filter all user input, or just particular keys.
Idea:
$this->input->filter([$myClass, $inputFilter->filter(...)])
to pass EVERY kvp through your callback.
$this->input->filter([$myClass, $inputFilter->filter(...)], "name", "postcode")
to only pass name and postcode kvps through your callback.
All other getters return a nullable type, depending on whether the field is present in the input.
getFile throws an exception if there isn't a file.
Make it return null instead, so it's consistent.
$this->input
->with("dateRange", "source", "level", "regex")
->call([$this, "setFilters"]);
The setFilters
function will receive a single parameter of type CombinedInputData
. It might be useful to use a function such as callWithParameters
so the setFilters
function is then called with individual parameters.
Receives all input parameters that start with the given prefix.
$input->do("save")
->withPrefix("io_")
->call("processIo");
function processIo(InputData $data) {
// $data["product_id"] // from input name=io_product_id
}
<form method="post">
<input name="io_product_id" />
<button name="do" value="save">Save</button>
</form>
Nobody will ever want no input - missing with/without obviously means withAll()
Taking the example from the readme:
$payment = new PaymentProcessor();
$input->do("pay")
->call([$payment, "processCard"]);
The $payment
object's processCard
function should receive an InputData
object with all of the fields by default, as none have been specified with
or without
.
Consolidate getter functions into this trait.
There is a trait called InputValueGetter which allows the get* methods to be shared with the InputData and Input class itself. A couple of functions have not been maintained correctly and need moving into this trait so all areas of the code are consistent.
It's not possible to unserialize FileUpload objects as they are stateless. This could be explained nicely in an exception.
$dateFrom = $input->getDateTime("date-from");
What type of DateTime should this library return in this case? I like the simplicity of DateTime
, and with it being a getter it probably doesn't matter about immutability of the object itself, but would it make more sense or would it potentially confuse someone if the returned object was DateTimeImmutable
?
$input->getFile()
will fail with a strange array-key exception if the form is not set with enctype='multipart/formdata'
, but this is not obvious and should be explained in a friendly exception message.
There's a dependency due to how File Uploads are defined in PSR-7, but is it strictly necessary to have the hard dependency to Gt/Http?
Following on from #15, now it is possible to create a trigger with just the keys, it would be nice to allow variable arguments to the when
function.
Examples:
$input->when("code"); // triggers when "code" is present
$input->when("code", "email"); // triggers when "code" and "email" are present
$input->when(["code" => "1234", "email"]); // triggers when "code" is "1234" and "email" is present
getMultipleFile and getMultipleDateTime exist, is it possible to retrieve multiple strings or integers?
UploadData should be included in the Input's internal data
collection, so that it can be referred to in exactly the same way as query string or request body parameters.
The added benefit of this means that callbacks can be passed dynamic functions that can benefit from the type juggling of PHP.
To achieve this, all parameters (apart from extras passed in to ->call()
) need to be InputDatum
types. An InputDatum is a representation of a single request KVP.
$input->do("update-settings")
->with("username", "password", "profile-photo")
->call("saveSettings");
function saveSettings(string $username, string $password, FileUpload $profilePhoto) {
// Dynamic properties with correct type, juggled by PHP.
}
There is currently no documentation. This is the outline I'd like to cover:
Get input constructed as given type with getAs
.
Force get
to cast InputDatum
to string - must use get*
to obtain special class.
Provide a list of classes to check for do*
methods, calling matching ones automatically.
If a
do
named parameter comes in, automatically call the matching callback.For example,
<button name="do" value="login">Login</button>
should automatically callYourPage::doLogin
, if it exists.
All other type-safe getters are nullable. My question is why the getFile
function in particular returns a non-nullable FileUpload
type - if there is no file at this input, shouldn't it be consistent and return null
?
Recently in a WebEngine project I've had to work with the uploading of seriously large files.
Allowing POST input larger than a gigabyte seems massively excessive, and has to be done on the php.ini level so can't be controlled per-script.
The solution was to use a PUT request, then fopen("php://input", "r")
to deal with the incoming data.
Could this be handled automatically by Input? The FileInput object could expose a getStream method that works under POST and PUT requests, but with PUT the file wouldn't automatically be loaded into memory.
When using with()
, what should happen if a key is requested that doesn't actually exist on the request?
It sounds like an exceptional circumstance to me, so there should probably be a MissingInputKeyException
or something like that thrown.
Add a test to InputTest.php
after testWithExist
called testWithNotExist
The combined data array should also contain files (loaded originally from $_FILES).
$fileObject = $input->get("my-photo"); // Gt\Input\File
$input->with("name", "my-photo")->call(function(InputData $data) {
echo $data["name"];
$data["my-photo"]->move("/tmp"); // move_uploaded_file
});
When moving an uploaded file, the data dir is not automatically created.
Imagine a user enters their credit card information into a form and presses submit. Unsetting the globals doesn't directly enhance security, as php://input is still available, and credit card information should not be able to be read at all by third party code.
Using openssl (or similar) to secure user input is the answer. Either on all forms, or opted in forms, security should be applied automatically.
It should work like this:
There are sometimes occasions where a query string parameter is present, but it is then overwritten by a POST field of the same name. Test that the POST will always overwrite the value of the GET value.
There needs to be protection against stupid inputs that could be used to break things.
Values should be fine, because they should be able to accept anything.
What if input keys contained HTML or SQL?
Just an idea for now.
Tests are quite slow to run, because there is so much random data generated in the PHPUnit dataProviders.
Hundreds of random input strings are generated in the unit tests, there are two improvements I can see:
If the default php.ini setting of upload_max_filesize is 2M, and a 3M file is uploaded, the exception is Gt\Input\UploadedFileSecurityException
. This is something we can test for, and if the file is larger than the smaller limit, we can throw an appropriate error message.
Dependabot needs to be tamed, as per PhpGt/WebEngine#568
Following on from #59, setting a callback for when a trigger is not fired is useful for terse code.
Example:
$this->input
->when("name", "age")
->call([$this, "setDetails"])
->or([$this, "setDefault"]);
do
callbacks are called automatically now, but if the name of the input contains a hyphen, a corresponding function name can't be found.
Handle hyphen and underscore separated words by converting do => do-something
to do => doSomething
This is necessary due to the PhpGt/Http usage.
We have some great functionality for accepting user input, but what about validating it?
API starting point ideas:
$rules = new ValidationRules()
->required("username", "password", "dob")
->minimum("password", 8)
->type("dob", ValidationRules::FORMAT_DATE);
$this->input->do("save-profile")
->validate($rules)
->with("username", "password", "profile-picture")
->call([$this, "saveProfile"])
->invalidCall([$this, "displayError"]);
I have no solid ideas of how this function could work yet, but passing it a ValidationRules object sounds like a good starting point.
This is currently done in WebEngine, and will need to be done in any code that uses this library, so provide a mechanism for this library to unset the globals automatically.
Currently the when
trigger mechanism requires to know the value.
$input->when(["something"])
can be used. Notice the non-associative array. This can be used to trigger when something
is set to any value.
Deprecated: Return type of Gt\Input\InputData\AbstractInputData::rewind() should either be compatible with Iterator::rewind(): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /home/g105b/Code/Project/website/vendor/phpgt/input/src/InputData/KeyValueIterator.php on line 26
Deprecated: Return type of Gt\Input\InputData\AbstractInputData::valid() should either be compatible with Iterator::valid(): bool, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /home/g105b/Code/Project/website/vendor/phpgt/input/src/InputData/KeyValueIterator.php on line 22
Deprecated: Return type of Gt\Input\InputData\AbstractInputData::offsetUnset($offset) should either be compatible with ArrayAccess::offsetUnset(mixed $offset): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /home/g105b/Code/Project/website/vendor/phpgt/input/src/InputData/KeyValueArrayAccess.php on line 45
Deprecated: Return type of Gt\Input\InputData\AbstractInputData::offsetGet($offset) should either be compatible with ArrayAccess::offsetGet(mixed $offset): mixed, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /home/g105b/Code/Project/website/vendor/phpgt/input/src/InputData/KeyValueArrayAccess.php on line 13
Deprecated: Return type of Gt\Input\Input::rewind() should either be compatible with Iterator::rewind(): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /home/g105b/Code/Project/website/vendor/phpgt/input/src/InputData/KeyValueIterator.php on line 26
Deprecated: Return type of Gt\Input\Input::valid() should either be compatible with Iterator::valid(): bool, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /home/g105b/Code/Project/website/vendor/phpgt/input/src/InputData/KeyValueIterator.php on line 22
Deprecated: Return type of Gt\Input\Input::offsetUnset($offset) should either be compatible with ArrayAccess::offsetUnset(mixed $offset): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /home/g105b/Code/Project/website/vendor/phpgt/input/src/InputData/KeyValueArrayAccess.php on line 45
Deprecated: Return type of Gt\Input\Input::offsetGet($offset) should either be compatible with ArrayAccess::offsetGet(mixed $offset): mixed, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /home/g105b/Code/Project/website/vendor/phpgt/input/src/InputData/KeyValueArrayAccess.php on line 13
Fatal error: Uncaught TypeError: Gt\Input\Input::getMultipleFile(): Return value must be of type Gt\Input\InputData\Datum\MultipleInputDatum, Gt\Input\InputData\Datum\InputDatum returned in /home/guy/insulmap/vendor/phpgt/input/src/InputValueGetter.php:73 Stack trace: #0 gt-logic-stream://page/project/@project/soft-cover/rfi/edit.php(188): Gt\Input\Input->getMultipleFile() #1 gt-logic-stream://page/project/@project/soft-cover/rfi/edit.php(93): InsulMap\Page\Project\Project\SoftCover\Rfi\EditPage->save() #2 [internal function]: InsulMap\Page\Project\Project\SoftCover\Rfi\EditPage->do_save_request() #3 /home/guy/insulmap/vendor/phpgt/servicecontainer/src/Injector.php(60): ReflectionMethod->invoke() #4 /home/guy/insulmap/vendor/phpgt/webengine/src/Logic/LogicExecutor.php(37): Gt\ServiceContainer\Injector->invoke() #5 /home/guy/insulmap/vendor/phpgt/webengine/src/Middleware/RequestHandler.php(221): Gt\WebEngine\Logic\LogicExecutor->invoke() #6 [internal function]: Gt\WebEngine\Middleware\RequestHandler->Gt\WebEngine\Middleware{closure}() #7 /home/guy/insulmap/vendor/phpgt/input/src/Trigger/Callback.php(25): call_user_func_array() #8 /home/guy/insulmap/vendor/phpgt/input/src/Trigger/Trigger.php(149): Gt\Input\Trigger\Callback->call() #9 /home/guy/insulmap/vendor/phpgt/input/src/Trigger/Trigger.php(134): Gt\Input\Trigger\Trigger->callCallbacks() #10 /home/guy/insulmap/vendor/phpgt/input/src/Trigger/Trigger.php(93): Gt\Input\Trigger\Trigger->fire() #11 /home/guy/insulmap/vendor/phpgt/webengine/src/Middleware/RequestHandler.php(223): Gt\Input\Trigger\Trigger->call() #12 /home/guy/insulmap/vendor/phpgt/webengine/src/Middleware/Lifecycle.php(91): Gt\WebEngine\Middleware\RequestHandler->handle() #13 /home/guy/insulmap/vendor/phpgt/webengine/src/Middleware/Lifecycle.php(67): Gt\WebEngine\Middleware\Lifecycle->process() #14 /home/guy/insulmap/vendor/phpgt/webengine/go.php(48): Gt\WebEngine\Middleware\Lifecycle->start() #15 {main} thrown in /home/guy/insulmap/vendor/phpgt/input/src/InputValueGetter.php on line 73
It's useful to be able to cast the entire input as an associative array without having to getAll()
first.
$input->has("something")
returns true even if "something" value is an empty string.
Improvement for when checking if there is a meaningful value: $input->hasNonEmpty("something");
I'm fleshing out the syntax for a more encapsulated user input model. Taking the "do" terminology from Gt v2, as it is short, fun and friendly.
This interaction model enforces encapsulation, meaning the developer writing the go
function must decide which areas of code receive user input, making it impossible for third party libraries to eavesdrop.
<h1>Hello, <span id="output">you</span>!</h1>
<form>
<input name="name" required placeholder="Your name" />
<button name="do" value="submit">Submit!</button>
</form>
public function go() {
// Somewhere to output the variable:
$outputTo = $this->document->getElementById("output");
// Trigger when "do=submit" is passed.
$this->input->do("submit")
// List the parameters to pass to the callback.
->with("name")
// Reference the callback, with optional added properties
->call([$this, "outputName"], $outputTo);
protected function outputName(InputFields $fields, Node $outputTo) {
// ...
}
<form method="post">
<input name="name" required />
<input name="postcode" required />
<input name="creditcard" required />
<button name="do" value="pay">Pay me your money</button>
</form>
public function go() {
$this->input->do("pay")
// All fields need passing to payment processor:
->withAll()
->call([Payment::class, "process"])
// ...but the user storage should not see credit card information.
->without("creditcard")
->call([User::class, "storeDetails"]);
}
public function go() {
$this->input->do("something")->callWithAll(function(InputFields $fields) {
foreach($fields as $field) {
someAction($field);
}
somethingElse($fields->fieldName);
});
}
For all typed InputGetter
functions apart from getString
, if the input is an empty string, the result should be null
.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.