Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions docs/index.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
* [Overview](overview.md)
* [Updating from v2 to v3](v2-to-v3-update.md)
* [Updating from v3 to v4](v3-to-v4-update.md)
* Guide
* [Overview](overview.md)
* Upgrading
* [Updating from v2 to v3](v2-to-v3-update.md)
* [Updating from v3 to v4](v3-to-v4-update.md)
167 changes: 165 additions & 2 deletions docs/overview.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,166 @@
# Input package
# Overview

TODO
The Input package is the request-side counterpart to the application's response: it wraps the
superglobals and returns filtered values through a single API.

```bash
composer require joomla/input
```

## What is in the package

| Class | Source | Purpose |
|---|---|---|
| `Joomla\Input\Input` | `$_REQUEST` | The base class; also the type of `$input->get`, `$input->post`, … |
| `Joomla\Input\Cookie` | `$_COOKIE` | Adds `set()` writing a real cookie |
| `Joomla\Input\Files` | `$_FILES` | Pivots the upload array into per-file entries |
| `Joomla\Input\Json` | `php://input` | Decodes a JSON request body |

## Reading values

Every read goes through a filter. The default is `cmd`:

```php
use Joomla\Input\Input;

$input = new Input();

$input->get('name'); // filtered with 'cmd'
$input->get('name', 'anonymous'); // with a default
$input->get('body', '', 'raw'); // unfiltered
```

The `get<Type>()` shorthands are resolved through `__call()`, so the type name is the filter name:

| Call | Filter |
|---|---|
| `getInt()`, `getUint()`, `getFloat()` | numeric |
| `getBool()` | boolean |
| `getWord()`, `getAlnum()`, `getCmd()` | restricted character sets |
| `getBase64()` | base64 alphabet |
| `getString()`, `getHtml()` | sanitised text |
| `getPath()` | file path |
| `getUsername()` | control characters and `<>"'%&` removed |
| `getRaw()` | nothing |

Anything else — `getFoobar()` — silently falls back to string filtering rather than raising an
error, so a typo in the method name returns a value instead of failing.

## The sub-inputs

Magic properties give access to the other superglobals, each wrapped in its own `Input`:

```php
$input->get->getInt('page'); // $_GET
$input->post->getString('title'); // $_POST
$input->server->getString('REQUEST_METHOD');
$input->env->getString('PATH');
$input->cookie->getString('lang'); // Joomla\Input\Cookie
$input->files->get('upload'); // Joomla\Input\Files
$input->json->getString('name'); // Joomla\Input\Json
```

Note that `isset($input->post)` returns `false` — the class implements `__get()` but not
`__isset()`.

## Writing values

```php
$input->set('view', 'articles'); // overwrite
$input->def('layout', 'default'); // only if not already present
$input->exists('view'); // true
count($input); // Countable
```

These change the `Input` object only; the superglobals are untouched.

## Reading several values at once

```php
$data = $input->getArray([
'id' => 'uint',
'title' => 'string',
'tags' => ['name' => 'string'], // nested
]);
```

Called without arguments, `getArray()` returns everything — but be aware that it then uses each
*value* as the filter name for its own key, which is not what the docblock describes. Pass an
explicit map whenever the result matters.

`getArray()` also assumes nested keys exist: `getArray(['a' => ['b' => 'int']], $source)` raises a
warning and a `TypeError` if `$source['a']` is missing. Supply defaults or check first.

## Request method

```php
$input->getMethod(); // 'GET', 'POST', …
$input->getInputForRequestMethod(); // the $_GET or $_POST input
```

For `PUT`, `PATCH` and `DELETE` there is no superglobal, so `getInputForRequestMethod()` returns
the `$_REQUEST`-backed instance — which means the request body of those methods is **not read at
all**. Use `Joomla\Input\Json` or parse `php://input` yourself for them.

## Cookies

```php
use Joomla\Input\Cookie;

$cookies = new Cookie();

$cookies->set('lang', 'de', [
'expires' => time() + 86400,
'path' => '/',
'secure' => true,
'httponly' => true,
'samesite' => 'Lax',
]);
```

The options array is handed straight to `setcookie()`. Nothing is added, so **omitting `secure`,
`httponly` or `samesite` means the cookie is written without them**. Always pass them explicitly.

The return value of `setcookie()` is discarded, so a failed call — headers already sent, invalid
name — still updates the object's own data and reports nothing.

## Uploaded files

```php
$file = $input->files->get('avatar');

// ['name' => …, 'type' => …, 'tmp_name' => …, 'error' => …, 'size' => …]
```

Multi-file inputs are pivoted, so `files->get('attachments')` returns a list of such arrays rather
than PHP's column-wise structure.

> **The filter argument is ignored.** `Files::get()` accepts a third `$filter` parameter and never
> applies it. The returned `name` and `type` are the raw, client-supplied values. Never build a
> path from `name` without sanitising it yourself, and never trust `type` — validate the real MIME
> type with `finfo` instead.

`Files::set()` is deliberately a no-op, but the inherited `def()` still writes, so file entries can
be injected into the object.

## JSON bodies

```php
use Joomla\Input\Json;

$json = new Json();

$json->getString('title');
$json->getRaw(); // the undecoded body
```

The body is read and decoded on construction regardless of the request's `Content-Type`. A body
that is not valid JSON yields an empty input rather than an error, so an API cannot distinguish
"malformed JSON" from "no fields sent" — check `getRaw()` if that matters.

## Not part of this package

* No PSR-7. There is no bridge from or to `ServerRequestInterface`.
* No `UploadedFileInterface`, no upload validation helpers (`UPLOAD_ERR_*`, size, MIME).
* No trusted-proxy handling for `X-Forwarded-For` or `X-Forwarded-Proto`.
* No `remove()`, no `ArrayAccess`, no `IteratorAggregate`.
37 changes: 34 additions & 3 deletions docs/v2-to-v3-update.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,36 @@
## Updating from v2 to v3
# Updating from v2 to v3

### Minimum supported PHP version raised
Release 3.0.0 raises the PHP requirement and reformats the codebase. **No public or protected
method signature changed**, so code written against 2.x keeps working on PHP 8.1.

All Framework packages now require PHP 8.1 or newer.
## At a glance

| | v2 (2.0.4) | v3 (3.0.0) |
|---|---|---|
| PHP | `^7.2.5` | `^8.1.0` |
| Public API | — | unchanged |
| Coding style | Joomla Coding Standard | PSR-12 |

## Minimum supported PHP version raised

All Framework packages now require **PHP 8.1** or newer.

## No API changes

Every method on `Input`, `Cookie`, `Files` and `Json` has the same signature in 3.0.0 as in 2.0.0.
The deprecated `Cookie::set()` signature described in the
[v3 to v4 guide](v3-to-v4-update.md) is still present and still emits a deprecation notice.

## Codebase converted to PSR-12

The package was reformatted from the Joomla Coding Standard to PSR-12. This touches nearly every
line and changes no behaviour, so a `git diff` between 2.x and 3.x is almost entirely noise. Use
`git diff -w` when looking for real changes.

## Dependency changes

| Package | v2 (2.0.4) | v3 (3.0.0) |
|---|---|---|
| `php` | `^7.2.5` | `^8.1.0` |
| `joomla/filter` | `^1.0 \| ^2.0` | `^3.0` |
| `symfony/deprecation-contracts` | `^2.1` | `^2 \| ^3` |
62 changes: 59 additions & 3 deletions docs/v3-to-v4-update.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,61 @@
## Updating from v3 to v4
# Updating from v3 to v4

### Minimum supported PHP version raised
Release 4.0.0 raises the PHP requirement and removes the legacy `Cookie::set()` signature that had
been deprecated since 1.4.0.

All Framework packages now require PHP 8.3 or newer.
## At a glance

| | v3 (3.0.2) | v4 (4.0.0) |
|---|---|---|
| PHP | `^8.1.0` | `^8.3.0` |
| `Cookie::set()` positional signature | deprecated, works | **removed** |
| `joomla/filter` | `^3.0` | `^4.0` |

## Minimum supported PHP version raised

All Framework packages now require **PHP 8.3** or newer.

## The legacy `Cookie::set()` signature was removed

Before 1.4.0 the method took the cookie attributes as positional arguments. 1.4.0 added the
options-array form and kept a compatibility layer that inspected `func_get_args()`; 4.0.0 removes
that layer.

```php
// Removed in 4.0.0 - the positional form
$cookie->set('lang', 'de', time() + 86400, '/', 'example.com', true, true);

// The options form, available since 1.4.0
$cookie->set('lang', 'de', [
'expires' => time() + 86400,
'path' => '/',
'domain' => 'example.com',
'secure' => true,
'httponly' => true,
'samesite' => 'Lax',
]);
```

Passing anything other than an array as the third argument now reaches `setcookie()` unchanged and
raises a `TypeError` there rather than being translated.

The options form supports `samesite`, which the positional form never could — worth setting while
you are changing these call sites anyway.

To find them:

```bash
grep -rn -- '->set(' src/ | grep -i cookie
```

## No other API changes

Apart from the removed compatibility layer, `Input`, `Cookie`, `Files` and `Json` are unchanged.

## Dependency changes

| Package | v3 (3.0.2) | v4 (4.0.0) |
|---|---|---|
| `php` | `^8.1.0` | `^8.3.0` |
| `joomla/filter` | `^3.0` | `^4.0` |
| `symfony/deprecation-contracts` | `^2 \| ^3` | unchanged |
Loading