/** * REST API: WP_REST_Request class * * @package WordPress * @subpackage REST_API * @since 4.4.0 */ /** * Core class used to implement a REST request object. * * Contains data from the request, to be passed to the callback. * * Note: This implements ArrayAccess, and acts as an array of parameters when * used in that manner. It does not use ArrayObject (as we cannot rely on SPL), * so be aware it may have non-array behaviour in some cases. * * Note: When using features provided by ArrayAccess, be aware that WordPress deliberately * does not distinguish between arguments of the same name for different request methods. * For instance, in a request with `GET id=1` and `POST id=2`, `$request['id']` will equal * 2 (`POST`) not 1 (`GET`). For more precision between request methods, use * WP_REST_Request::get_body_params(), WP_REST_Request::get_url_params(), etc. * * @since 4.4.0 * * @see ArrayAccess */ class WP_REST_Request implements ArrayAccess { /** * HTTP method. * * @since 4.4.0 * @var string */ protected $method = ''; /** * Parameters passed to the request. * * These typically come from the `$_GET`, `$_POST` and `$_FILES` * superglobals when being created from the global scope. * * @since 4.4.0 * @var array Contains GET, POST and FILES keys mapping to arrays of data. */ protected $params; /** * HTTP headers for the request. * * @since 4.4.0 * @var array Map of key to value. Key is always lowercase, as per HTTP specification. */ protected $headers = array(); /** * Body data. * * @since 4.4.0 * @var string Binary data from the request. */ protected $body = null; /** * Route matched for the request. * * @since 4.4.0 * @var string */ protected $route; /** * Attributes (options) for the route that was matched. * * This is the options array used when the route was registered, typically * containing the callback as well as the valid methods for the route. * * @since 4.4.0 * @var array Attributes for the request. */ protected $attributes = array(); /** * Used to determine if the JSON data has been parsed yet. * * Allows lazy-parsing of JSON data where possible. * * @since 4.4.0 * @var bool */ protected $parsed_json = false; /** * Used to determine if the body data has been parsed yet. * * @since 4.4.0 * @var bool */ protected $parsed_body = false; /** * Constructor. * * @since 4.4.0 * * @param string $method Optional. Request method. Default empty. * @param string $route Optional. Request route. Default empty. * @param array $attributes Optional. Request attributes. Default empty array. */ public function __construct( $method = '', $route = '', $attributes = array() ) { $this->params = array( 'URL' => array(), 'GET' => array(), 'POST' => array(), 'FILES' => array(), // See parse_json_params. 'JSON' => null, 'defaults' => array(), ); $this->set_method( $method ); $this->set_route( $route ); $this->set_attributes( $attributes ); } /** * Retrieves the HTTP method for the request. * * @since 4.4.0 * * @return string HTTP method. */ public function get_method() { return $this->method; } /** * Sets HTTP method for the request. * * @since 4.4.0 * * @param string $method HTTP method. */ public function set_method( $method ) { $this->method = strtoupper( $method ); } /** * Retrieves all headers from the request. * * @since 4.4.0 * * @return array Map of key to value. Key is always lowercase, as per HTTP specification. */ public function get_headers() { return $this->headers; } /** * Canonicalizes the header name. * * Ensures that header names are always treated the same regardless of * source. Header names are always case insensitive. * * Note that we treat `-` (dashes) and `_` (underscores) as the same * character, as per header parsing rules in both Apache and nginx. * * @link https://stackoverflow.com/q/18185366 * @link https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/#missing-disappearing-http-headers * @link https://nginx.org/en/docs/http/ngx_http_core_module.html#underscores_in_headers * * @since 4.4.0 * @static * * @param string $key Header name. * @return string Canonicalized name. */ public static function canonicalize_header_name( $key ) { $key = strtolower( $key ); $key = str_replace( '-', '_', $key ); return $key; } /** * Retrieves the given header from the request. * * If the header has multiple values, they will be concatenated with a comma * as per the HTTP specification. Be aware that some non-compliant headers * (notably cookie headers) cannot be joined this way. * * @since 4.4.0 * * @param string $key Header name, will be canonicalized to lowercase. * @return string|null String value if set, null otherwise. */ public function get_header( $key ) { $key = $this->canonicalize_header_name( $key ); if ( ! isset( $this->headers[ $key ] ) ) { return null; } return implode( ',', $this->headers[ $key ] ); } /** * Retrieves header values from the request. * * @since 4.4.0 * * @param string $key Header name, will be canonicalized to lowercase. * @return array|null List of string values if set, null otherwise. */ public function get_header_as_array( $key ) { $key = $this->canonicalize_header_name( $key ); if ( ! isset( $this->headers[ $key ] ) ) { return null; } return $this->headers[ $key ]; } /** * Sets the header on request. * * @since 4.4.0 * * @param string $key Header name. * @param string $value Header value, or list of values. */ public function set_header( $key, $value ) { $key = $this->canonicalize_header_name( $key ); $value = (array) $value; $this->headers[ $key ] = $value; } /** * Appends a header value for the given header. * * @since 4.4.0 * * @param string $key Header name. * @param string $value Header value, or list of values. */ public function add_header( $key, $value ) { $key = $this->canonicalize_header_name( $key ); $value = (array) $value; if ( ! isset( $this->headers[ $key ] ) ) { $this->headers[ $key ] = array(); } $this->headers[ $key ] = array_merge( $this->headers[ $key ], $value ); } /** * Removes all values for a header. * * @since 4.4.0 * * @param string $key Header name. */ public function remove_header( $key ) { $key = $this->canonicalize_header_name( $key ); unset( $this->headers[ $key ] ); } /** * Sets headers on the request. * * @since 4.4.0 * * @param array $headers Map of header name to value. * @param bool $override If true, replace the request's headers. Otherwise, merge with existing. */ public function set_headers( $headers, $override = true ) { if ( true === $override ) { $this->headers = array(); } foreach ( $headers as $key => $value ) { $this->set_header( $key, $value ); } } /** * Retrieves the content-type of the request. * * @since 4.4.0 * * @return array Map containing 'value' and 'parameters' keys. */ public function get_content_type() { $value = $this->get_header( 'content-type' ); if ( empty( $value ) ) { return null; } $parameters = ''; if ( strpos( $value, ';' ) ) { list( $value, $parameters ) = explode( ';', $value, 2 ); } $value = strtolower( $value ); if ( strpos( $value, '/' ) === false ) { return null; } // Parse type and subtype out. list( $type, $subtype ) = explode( '/', $value, 2 ); $data = compact( 'value', 'type', 'subtype', 'parameters' ); $data = array_map( 'trim', $data ); return $data; } /** * Retrieves the parameter priority order. * * Used when checking parameters in get_param(). * * @since 4.4.0 * * @return array List of types to check, in order of priority. */ protected function get_parameter_order() { $order = array(); $content_type = $this->get_content_type(); if ( $content_type['value'] === 'application/json' ) { $order[] = 'JSON'; } $this->parse_json_params(); // Ensure we parse the body data. $body = $this->get_body(); if ( 'POST' !== $this->method && ! empty( $body ) ) { $this->parse_body_params(); } $accepts_body_data = array( 'POST', 'PUT', 'PATCH', 'DELETE' ); if ( in_array( $this->method, $accepts_body_data ) ) { $order[] = 'POST'; } $order[] = 'GET'; $order[] = 'URL'; $order[] = 'defaults'; /** * Filters the parameter order. * * The order affects which parameters are checked when using get_param() and family. * This acts similarly to PHP's `request_order` setting. * * @since 4.4.0 * * @param array $order { * An array of types to check, in order of priority. * * @param string $type The type to check. * } * @param WP_REST_Request $this The request object. */ return apply_filters( 'rest_request_parameter_order', $order, $this ); } /** * Retrieves a parameter from the request. * * @since 4.4.0 * * @param string $key Parameter name. * @return mixed|null Value if set, null otherwise. */ public function get_param( $key ) { $order = $this->get_parameter_order(); foreach ( $order as $type ) { // Determine if we have the parameter for this type. if ( isset( $this->params[ $type ][ $key ] ) ) { return $this->params[ $type ][ $key ]; } } return null; } /** * Sets a parameter on the request. * * @since 4.4.0 * * @param string $key Parameter name. * @param mixed $value Parameter value. */ public function set_param( $key, $value ) { $order = $this->get_parameter_order(); $this->params[ $order[0] ][ $key ] = $value; } /** * Retrieves merged parameters from the request. * * The equivalent of get_param(), but returns all parameters for the request. * Handles merging all the available values into a single array. * * @since 4.4.0 * * @return array Map of key to value. */ public function get_params() { $order = $this->get_parameter_order(); $order = array_reverse( $order, true ); $params = array(); foreach ( $order as $type ) { // array_merge / the "+" operator will mess up // numeric keys, so instead do a manual foreach. foreach ( (array) $this->params[ $type ] as $key => $value ) { $params[ $key ] = $value; } } return $params; } /** * Retrieves parameters from the route itself. * * These are parsed from the URL using the regex. * * @since 4.4.0 * * @return array Parameter map of key to value. */ public function get_url_params() { return $this->params['URL']; } /** * Sets parameters from the route. * * Typically, this is set after parsing the URL. * * @since 4.4.0 * * @param array $params Parameter map of key to value. */ public function set_url_params( $params ) { $this->params['URL'] = $params; } /** * Retrieves parameters from the query string. * * These are the parameters you'd typically find in `$_GET`. * * @since 4.4.0 * * @return array Parameter map of key to value */ public function get_query_params() { return $this->params['GET']; } /** * Sets parameters from the query string. * * Typically, this is set from `$_GET`. * * @since 4.4.0 * * @param array $params Parameter map of key to value. */ public function set_query_params( $params ) { $this->params['GET'] = $params; } /** * Retrieves parameters from the body. * * These are the parameters you'd typically find in `$_POST`. * * @since 4.4.0 * * @return array Parameter map of key to value. */ public function get_body_params() { return $this->params['POST']; } /** * Sets parameters from the body. * * Typically, this is set from `$_POST`. * * @since 4.4.0 * * @param array $params Parameter map of key to value. */ public function set_body_params( $params ) { $this->params['POST'] = $params; } /** * Retrieves multipart file parameters from the body. * * These are the parameters you'd typically find in `$_FILES`. * * @since 4.4.0 * * @return array Parameter map of key to value */ public function get_file_params() { return $this->params['FILES']; } /** * Sets multipart file parameters from the body. * * Typically, this is set from `$_FILES`. * * @since 4.4.0 * * @param array $params Parameter map of key to value. */ public function set_file_params( $params ) { $this->params['FILES'] = $params; } /** * Retrieves the default parameters. * * These are the parameters set in the route registration. * * @since 4.4.0 * * @return array Parameter map of key to value */ public function get_default_params() { return $this->params['defaults']; } /** * Sets default parameters. * * These are the parameters set in the route registration. * * @since 4.4.0 * * @param array $params Parameter map of key to value. */ public function set_default_params( $params ) { $this->params['defaults'] = $params; } /** * Retrieves the request body content. * * @since 4.4.0 * * @return string Binary data from the request body. */ public function get_body() { return $this->body; } /** * Sets body content. * * @since 4.4.0 * * @param string $data Binary data from the request body. */ public function set_body( $data ) { $this->body = $data; // Enable lazy parsing. $this->parsed_json = false; $this->parsed_body = false; $this->params['JSON'] = null; } /** * Retrieves the parameters from a JSON-formatted body. * * @since 4.4.0 * * @return array Parameter map of key to value. */ public function get_json_params() { // Ensure the parameters have been parsed out. $this->parse_json_params(); return $this->params['JSON']; } /** * Parses the JSON parameters. * * Avoids parsing the JSON data until we need to access it. * * @since 4.4.0 * @since 4.7.0 Returns error instance if value cannot be decoded. * @return true|WP_Error True if the JSON data was passed or no JSON data was provided, WP_Error if invalid JSON was passed. */ protected function parse_json_params() { if ( $this->parsed_json ) { return true; } $this->parsed_json = true; // Check that we actually got JSON. $content_type = $this->get_content_type(); if ( empty( $content_type ) || 'application/json' !== $content_type['value'] ) { return true; } $body = $this->get_body(); if ( empty( $body ) ) { return true; } $params = json_decode( $body, true ); /* * Check for a parsing error. * * Note that due to WP's JSON compatibility functions, json_last_error * might not be defined: https://core.trac.wordpress.org/ticket/27799 */ if ( null === $params && ( ! function_exists( 'json_last_error' ) || JSON_ERROR_NONE !== json_last_error() ) ) { // Ensure subsequent calls receive error instance. $this->parsed_json = false; $error_data = array( 'status' => WP_Http::BAD_REQUEST, ); if ( function_exists( 'json_last_error' ) ) { $error_data['json_error_code'] = json_last_error(); $error_data['json_error_message'] = json_last_error_msg(); } return new WP_Error( 'rest_invalid_json', __( 'Invalid JSON body passed.' ), $error_data ); } $this->params['JSON'] = $params; return true; } /** * Parses the request body parameters. * * Parses out URL-encoded bodies for request methods that aren't supported * natively by PHP. In PHP 5.x, only POST has these parsed automatically. * * @since 4.4.0 */ protected function parse_body_params() { if ( $this->parsed_body ) { return; } $this->parsed_body = true; /* * Check that we got URL-encoded. Treat a missing content-type as * URL-encoded for maximum compatibility. */ $content_type = $this->get_content_type(); if ( ! empty( $content_type ) && 'application/x-www-form-urlencoded' !== $content_type['value'] ) { return; } parse_str( $this->get_body(), $params ); /* * Amazingly, parse_str follows magic quote rules. Sigh. * * NOTE: Do not refactor to use `wp_unslash`. */ if ( get_magic_quotes_gpc() ) { $params = stripslashes_deep( $params ); } /* * Add to the POST parameters stored internally. If a user has already * set these manually (via `set_body_params`), don't override them. */ $this->params['POST'] = array_merge( $params, $this->params['POST'] ); } /** * Retrieves the route that matched the request. * * @since 4.4.0 * * @return string Route matching regex. */ public function get_route() { return $this->route; } /** * Sets the route that matched the request. * * @since 4.4.0 * * @param string $route Route matching regex. */ public function set_route( $route ) { $this->route = $route; } /** * Retrieves the attributes for the request. * * These are the options for the route that was matched. * * @since 4.4.0 * * @return array Attributes for the request. */ public function get_attributes() { return $this->attributes; } /** * Sets the attributes for the request. * * @since 4.4.0 * * @param array $attributes Attributes for the request. */ public function set_attributes( $attributes ) { $this->attributes = $attributes; } /** * Sanitizes (where possible) the params on the request. * * This is primarily based off the sanitize_callback param on each registered * argument. * * @since 4.4.0 * * @return true|WP_Error True if parameters were sanitized, WP_Error if an error occurred during sanitization. */ public function sanitize_params() { $attributes = $this->get_attributes(); // No arguments set, skip sanitizing. if ( empty( $attributes['args'] ) ) { return true; } $order = $this->get_parameter_order(); $invalid_params = array(); foreach ( $order as $type ) { if ( empty( $this->params[ $type ] ) ) { continue; } foreach ( $this->params[ $type ] as $key => $value ) { if ( ! isset( $attributes['args'][ $key ] ) ) { continue; } $param_args = $attributes['args'][ $key ]; // If the arg has a type but no sanitize_callback attribute, default to rest_parse_request_arg. if ( ! array_key_exists( 'sanitize_callback', $param_args ) && ! empty( $param_args['type'] ) ) { $param_args['sanitize_callback'] = 'rest_parse_request_arg'; } // If there's still no sanitize_callback, nothing to do here. if ( empty( $param_args['sanitize_callback'] ) ) { continue; } $sanitized_value = call_user_func( $param_args['sanitize_callback'], $value, $this, $key ); if ( is_wp_error( $sanitized_value ) ) { $invalid_params[ $key ] = $sanitized_value->get_error_message(); } else { $this->params[ $type ][ $key ] = $sanitized_value; } } } if ( $invalid_params ) { return new WP_Error( 'rest_invalid_param', sprintf( __( 'Invalid parameter(s): %s' ), implode( ', ', array_keys( $invalid_params ) ) ), array( 'status' => 400, 'params' => $invalid_params ) ); } return true; } /** * Checks whether this request is valid according to its attributes. * * @since 4.4.0 * * @return bool|WP_Error True if there are no parameters to validate or if all pass validation, * WP_Error if required parameters are missing. */ public function has_valid_params() { // If JSON data was passed, check for errors. $json_error = $this->parse_json_params(); if ( is_wp_error( $json_error ) ) { return $json_error; } $attributes = $this->get_attributes(); $required = array(); // No arguments set, skip validation. if ( empty( $attributes['args'] ) ) { return true; } foreach ( $attributes['args'] as $key => $arg ) { $param = $this->get_param( $key ); if ( isset( $arg['required'] ) && true === $arg['required'] && null === $param ) { $required[] = $key; } } if ( ! empty( $required ) ) { return new WP_Error( 'rest_missing_callback_param', sprintf( __( 'Missing parameter(s): %s' ), implode( ', ', $required ) ), array( 'status' => 400, 'params' => $required ) ); } /* * Check the validation callbacks for each registered arg. * * This is done after required checking as required checking is cheaper. */ $invalid_params = array(); foreach ( $attributes['args'] as $key => $arg ) { $param = $this->get_param( $key ); if ( null !== $param && ! empty( $arg['validate_callback'] ) ) { $valid_check = call_user_func( $arg['validate_callback'], $param, $this, $key ); if ( false === $valid_check ) { $invalid_params[ $key ] = __( 'Invalid parameter.' ); } if ( is_wp_error( $valid_check ) ) { $invalid_params[ $key ] = $valid_check->get_error_message(); } } } if ( $invalid_params ) { return new WP_Error( 'rest_invalid_param', sprintf( __( 'Invalid parameter(s): %s' ), implode( ', ', array_keys( $invalid_params ) ) ), array( 'status' => 400, 'params' => $invalid_params ) ); } return true; } /** * Checks if a parameter is set. * * @since 4.4.0 * * @param string $offset Parameter name. * @return bool Whether the parameter is set. */ public function offsetExists( $offset ) { $order = $this->get_parameter_order(); foreach ( $order as $type ) { if ( isset( $this->params[ $type ][ $offset ] ) ) { return true; } } return false; } /** * Retrieves a parameter from the request. * * @since 4.4.0 * * @param string $offset Parameter name. * @return mixed|null Value if set, null otherwise. */ public function offsetGet( $offset ) { return $this->get_param( $offset ); } /** * Sets a parameter on the request. * * @since 4.4.0 * * @param string $offset Parameter name. * @param mixed $value Parameter value. */ public function offsetSet( $offset, $value ) { $this->set_param( $offset, $value ); } /** * Removes a parameter from the request. * * @since 4.4.0 * * @param string $offset Parameter name. */ public function offsetUnset( $offset ) { $order = $this->get_parameter_order(); // Remove the offset from every group. foreach ( $order as $type ) { unset( $this->params[ $type ][ $offset ] ); } } /** * Retrieves a WP_REST_Request object from a full URL. * * @static * @since 4.5.0 * * @param string $url URL with protocol, domain, path and query args. * @return WP_REST_Request|false WP_REST_Request object on success, false on failure. */ public static function from_url( $url ) { $bits = parse_url( $url ); $query_params = array(); if ( ! empty( $bits['query'] ) ) { wp_parse_str( $bits['query'], $query_params ); } $api_root = rest_url(); if ( get_option( 'permalink_structure' ) && 0 === strpos( $url, $api_root ) ) { // Pretty permalinks on, and URL is under the API root. $api_url_part = substr( $url, strlen( untrailingslashit( $api_root ) ) ); $route = parse_url( $api_url_part, PHP_URL_PATH ); } elseif ( ! empty( $query_params['rest_route'] ) ) { // ?rest_route=... set directly $route = $query_params['rest_route']; unset( $query_params['rest_route'] ); } $request = false; if ( ! empty( $route ) ) { $request = new WP_REST_Request( 'GET', $route ); $request->set_query_params( $query_params ); } /** * Filters the request generated from a URL. * * @since 4.5.0 * * @param WP_REST_Request|false $request Generated request object, or false if URL * could not be parsed. * @param string $url URL the request was generated from. */ return apply_filters( 'rest_request_from_url', $request, $url ); } } Les limbes d’Icare | Chloé Dubreuil

Les limbes d’Icare

Extrait audio

An 402 après le Grand Enfermement, Icare est condamné. Parce qu’il a contrevenu à l’une des lois fondamentales de sa société, il devra dorénavant se heurter à tout ce que son monde avait éradiqué : la maladie, la douleur. La vieillesse, la mort.

Exilé parmi le Peuple sans Nom, ce peuple qui a recouvert de bidonvilles le vieux monde, Icare se lance dans un road-trip halluciné qui le mènera au-delà de l’océan, à la découverte de lui-même et du pire comme du meilleur de l’Homme. Un périple sans retour avec au bout, peut-être, l’idée de liberté et tout l’amour d’une enfant.

 

Ce récit surprenant, à mi-chemin entre la dystopie et le roman d’initiation, fait vibrer la corde sensible de son lecteur en conjuguant avec aisance le rêve et le cauchemar. La quête de vie menée par Icare est de celles qui se veulent à la fois tendres, fulgurantes et féroces, et qui résonnent encore longtemps en nous après avoir fermé la dernière page du livre.

 

CRITIQUES:

Mes promenades culturelles :

« Vous connaissez certainement Chloé Dubreuil si vous me suivez car ce n’est pas le premier roman dont je fais la chronique. En revanche, il s’agit de la première dystopie que je lis, écrite par ses soins.
En l’An 402 après le Grand Enfermement, Icare est condamné. Il est emmené de l’autre côté du mur et est condamné à errer parmi l’autre peuple, celui qui éprouve toutes les douleurs, les maladies, qui vit dans la misère la plus complète et dans une déchéance sans nom. Icare arrivera-t-il à se faire une place dans cette société inconnue ? Supportera-t-il de souffrir ?
Comme d’habitude, lorsque je lis un livre de Chloé, que je remercie pour sa confiance renouvelée, je ne vois pas passer les heures. Ce fut également le cas pour celui-ci.
J’aime beaucoup les dystopies et je peux vous assurer que celle-ci ne laisse pas indifférent ! Âmes sensibles s’abstenir ! Je ne regrette qu’une chose : en avoir déjà fini la lecture ! »

 

EXTRAIT

Centre de répanouissement

Continent intérieur

Année 402 depuis le Grand Enfermement

 

 

La B+ Jolana P. inclina insensiblement la tête. Dans ses iris bleutés prenait forme le songe de son patient.

Coulé dans un fauteuil microfibre, l’homme avait les yeux qui se déplaçaient par à-coups dans leurs orbites, il rêvait. Un rêve éveillé qui s’avérait un vrai nid de chimères. La tête de Jolana P. s’inclina plus insensiblement encore. Ses lèvres pleines se plissèrent en une moue d’enfant contrariée. Au fond de ses prunelles se reflétait toujours ce que voyait son patient : une paire d’ailes écarlates qui s’ouvrait démesurément, comme pour absorber la terre tout entière.

Inutile de l’interroger, les mots n’avaient pas d’utilité ; la connexion établie d’un cerveau à l’autre suffisait à enclore cette conscience en train de lui livrer ses audaces. L’homme était dangereux, un cas qui s’obstinait à penser un ailleurs interdit, révolu. Le processus de répanouissement ne fonctionnait pas.

I.T réfractaire. Sujet en phase 3. Phase terminale.

Impasse.

– Nous en avons terminé pour aujourd’hui, Icare.

Au son de la voix de son éthicienne, l’homme battit paresseusement des paupières. Un éclat d’obsidienne se mit à luire sous les sourcils bombés, le visage se crispa fugacement avant de se relâcher pour ne plus afficher qu’indifférence. Le visage d’Icare était parfaitement ciselé sous le casque de cheveux ébouriffés. Il s’en dégageait un mélange de délicatesse et de vigueur éminemment séduisant. C’était un visage lissé par le temps qui filait et ne comptait pas.

L’homme paraissait encore jeune, nettement plus jeune qu’il ne l’était en vérité et c’était bien à cause de cette jeunesse désespérante qu’il se retrouvait depuis neuf jours sous la coupe de la B+ Jolana P.

Celle-ci ôta de son crâne l’Extension qui reliait son néocortex à celui de son sujet et la posa sur une console d’un blanc laiteux. Icare cloua son regard à cette puce noire dont le double s’imbriquait toujours dans la masse de ses cheveux couleur de bière. Elle semblait à présent étrangement anecdotique et pourtant…

Un œil… L’œil de Dieu ! N’est-ce pas ainsi que nos ancêtres l’auraient appelée ?! Dieu, qui voit tout. P. voit tout de moi. Elle sait combien mon amertume est grande. Elle sait qu’elle ne la guérira pas.

Icare eut une brusque nausée et l’envie de nouveau, soudaine, éperdue, d’en finir.

Mourir enfin…

Mais une fois encore son corps le défia. Sous l’action instantanée des nanorobots qui patrouillaient au sein de son métabolisme, le sursaut d’angoisse se dissipa. Le regard de l’homme vira de bord pour se reporter sur l’éthicienne. Elle lisait en lui son aspiration, ce sentiment d’être piégé à l’intérieur de son éternité.

Ils n’étaient pas éternels, manqua-t-elle lui signifier, mais elle n’avait pas droit à de telles assertions. Leur civilisation n’avait-elle pas réussi à faire plier la Vie elle-même ?! À quel mensonge se serait-elle prêté si elle lui avait déclaré que leur longévité n’était pas essentielle à leur évolution ?!

La B+ Jolana P. retint les mots qui lui venaient aux lèvres. Jamais aucun patient n’avait fait osciller ses certitudes, mieux valait en terminer avec celui-ci, et vite.

– J’ai demandé à ce que votre dossier soit réenvisagé à la réunion de ce soir avec le comité.

L’éthicienne se dirigea vers l’embrasure de la porte coulissante. Le timbre de sa voix se fit plus traînant, vaguement sentencieux au moment d’ajouter :

– Notre décision vous sera rendue demain, nous ferons en sorte de ne pas oublier quelle bienveillance régit notre nation…

Un témoin électronique s’alluma, la porte coulissa sur ses rails, la silhouette de la praticienne disparut furtivement dans le couloir puis la porte se referma derrière elle en silence. Tout le bâtiment crachait le même silence somnolent. Icare ne bougea pas du fauteuil. Depuis neuf jours, il n’était pas sorti de cette chambre – cellule, n’avait croisé aucun autre inhibé, rencontré aucun autre spécialiste du Centre. Depuis neuf jours, Icare n’avait plus de communication avec l’extérieur – même son capteur personnel lui avait été enlevé. Son univers s’était rétréci à une pièce de quatre mètres sur cinq que lustrait chaque matin un robot volant autonome sorti de sa ruche high-tech. Depuis son placement au Centre de Répanouissement, Icare n’avait eu d’autre interlocuteur que Jolana P.

L’éthicienne (troisième échelon, B+, dans la hiérarchie des ingénieurs moraux assermentés pour ce type de névrose) s’évertuait à décortiquer ses projections, à labourer son dégoût de ce qu’il était, de ce qu’ils étaient tous sur le Continent intérieur. Et au-delà de celui-ci, qu’en était-il ?

Comme la vie lui pesait ! Un néant terrestre qui les muselait tous en ce Grand Pays. Il avait voulu s’évader, déserter cette existence où il demeurait figé en lui-même.

La grande désobéissance…

La belle humilité…

Il avait misé.

Il avait perdu.

 

*

 

Tout perdu, c’est ce qu’il avait cru sur le moment quand ils étaient venus l’appréhender dans ce qui aurait dû être son tombeau. C’était une erreur.

Icare s’obstinait à ne pas se lever du fauteuil, focalisé sur une unique pensée : tout allait prendre sens enfin. Quel terme désuet ! Donner du sens, un but à ce qui n’en avait pas eu…

Icare n’en doutait plus à présent, il n’aurait plus à se battre demain contre ce qu’il était. Demain lui serait concédée sa lassitude, les membres du comité courberaient l’échine devant ces ailes immenses et écarlates qu’il voyait en rêve. N’était-ce pas ce qu’ils avaient de plus sensé à faire ?!

Dans la chambre, une unique fenêtre ouvrait sur l’extérieur. Sa plaque de verre irisé s’oubliait sur un horizon peuplé de hauts arbres. Icare resta longtemps le regard rivé à la cime de leur feuillage jusqu’à ce que l’ombre des végétaux s’allongeât dans un poudroiement d’ocre.

Quand la pièce se ressentit des effets du crépuscule, il commanda à haute voix la mise en marche d’un programme nature. Autour de lui, les cloisons tapissées d’écrans souples se transformèrent en écheveaux de cascades et d’herbe folle, de landes arasées, de plaines sauvages étirées au pied de montagnes jurant sur l’azur. C’était tout juste si Icare ne sentait pas le vent couler à longs traits glacés dans sa gorge. Il eut un léger rire, soyeux, élégant, et qui parut ne pas finir.

Quelques heures encore et ces paysages artificiels l’emporteraient vers sa mort. Une poignée d’heures encore et le couperet tomberait sur son horloge biologique, il en était sûr cette fois.

 

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Required fields are marked *