From 31e66fb310d229f612a586942e665f0e14c10f0d Mon Sep 17 00:00:00 2001 From: DavidDevGt Date: Thu, 5 Sep 2024 12:01:59 -0600 Subject: [PATCH] Agregar archivo .gitignore y eliminar archivos innecesarios --- .gitignore | 21 ++ composer.json | 21 ++ composer.lock | 388 ++++++++++++++++++++++ public/css/main.css | 128 +++++++ public/index.html | 73 ++++ public/index.php | 94 ++++++ public/js/index.js | 109 ++++++ src/Adapters/JsonAdapter.php | 18 + src/Adapters/PhpSerializedAdapter.php | 17 + src/Adapters/XmlAdapter.php | 35 ++ src/Converter/FormatConverter.php | 46 +++ src/Interfaces/FormatAdapterInterface.php | 22 ++ src/Utils/ArrayToXml.php | 28 ++ 13 files changed, 1000 insertions(+) create mode 100644 .gitignore create mode 100644 composer.json create mode 100644 composer.lock create mode 100644 public/css/main.css create mode 100644 public/index.html create mode 100644 public/index.php create mode 100644 public/js/index.js create mode 100644 src/Adapters/JsonAdapter.php create mode 100644 src/Adapters/PhpSerializedAdapter.php create mode 100644 src/Adapters/XmlAdapter.php create mode 100644 src/Converter/FormatConverter.php create mode 100644 src/Interfaces/FormatAdapterInterface.php create mode 100644 src/Utils/ArrayToXml.php diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7750ad5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,21 @@ +/vendor/ +composer.lock + +*.log + +.vscode/ +.idea/ +*.sublime-workspace +*.sublime-project + +.DS_Store +Thumbs.db + +.phpunit.result.cache + +/public/storage +/storage/*.key +/storage/*.log + +.env +.env.* diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..4ef5e6b --- /dev/null +++ b/composer.json @@ -0,0 +1,21 @@ +{ + "name": "david/format_converter", + "description": "Conversion entre formatos XML, JSON y PHP (Serializado)", + "autoload": { + "psr-4": { + "FormatConverter\\": "src/" + } + }, + "authors": [ + { + "name": "DavidDevGt", + "email": "josuedavidvl18@gmail.com" + } + ], + "minimum-stability": "stable", + "require": { + "symfony/yaml": "^6.4", + "psr/log": "^3.0", + "monolog/monolog": "^3.7" + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..9ae2979 --- /dev/null +++ b/composer.lock @@ -0,0 +1,388 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "a020b975918320b15b7d8cf8286212c1", + "packages": [ + { + "name": "monolog/monolog", + "version": "3.7.0", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/monolog.git", + "reference": "f4393b648b78a5408747de94fca38beb5f7e9ef8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/f4393b648b78a5408747de94fca38beb5f7e9ef8", + "reference": "f4393b648b78a5408747de94fca38beb5f7e9ef8", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/log": "^2.0 || ^3.0" + }, + "provide": { + "psr/log-implementation": "3.0.0" + }, + "require-dev": { + "aws/aws-sdk-php": "^3.0", + "doctrine/couchdb": "~1.0@dev", + "elasticsearch/elasticsearch": "^7 || ^8", + "ext-json": "*", + "graylog2/gelf-php": "^1.4.2 || ^2.0", + "guzzlehttp/guzzle": "^7.4.5", + "guzzlehttp/psr7": "^2.2", + "mongodb/mongodb": "^1.8", + "php-amqplib/php-amqplib": "~2.4 || ^3", + "phpstan/phpstan": "^1.9", + "phpstan/phpstan-deprecation-rules": "^1.0", + "phpstan/phpstan-strict-rules": "^1.4", + "phpunit/phpunit": "^10.5.17", + "predis/predis": "^1.1 || ^2", + "ruflin/elastica": "^7", + "symfony/mailer": "^5.4 || ^6", + "symfony/mime": "^5.4 || ^6" + }, + "suggest": { + "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", + "doctrine/couchdb": "Allow sending log messages to a CouchDB server", + "elasticsearch/elasticsearch": "Allow sending log messages to an Elasticsearch server via official client", + "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", + "ext-curl": "Required to send log messages using the IFTTTHandler, the LogglyHandler, the SendGridHandler, the SlackWebhookHandler or the TelegramBotHandler", + "ext-mbstring": "Allow to work properly with unicode symbols", + "ext-mongodb": "Allow sending log messages to a MongoDB server (via driver)", + "ext-openssl": "Required to send log messages using SSL", + "ext-sockets": "Allow sending log messages to a Syslog server (via UDP driver)", + "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", + "mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)", + "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", + "rollbar/rollbar": "Allow sending log messages to Rollbar", + "ruflin/elastica": "Allow sending log messages to an Elastic Search server" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Monolog\\": "src/Monolog" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "https://seld.be" + } + ], + "description": "Sends your logs to files, sockets, inboxes, databases and various web services", + "homepage": "https://github.com/Seldaek/monolog", + "keywords": [ + "log", + "logging", + "psr-3" + ], + "support": { + "issues": "https://github.com/Seldaek/monolog/issues", + "source": "https://github.com/Seldaek/monolog/tree/3.7.0" + }, + "funding": [ + { + "url": "https://github.com/Seldaek", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/monolog/monolog", + "type": "tidelift" + } + ], + "time": "2024-06-28T09:40:51+00:00" + }, + { + "name": "psr/log", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "79dff0b268932c640297f5208d6298f71855c03e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/79dff0b268932c640297f5208d6298f71855c03e", + "reference": "79dff0b268932c640297f5208d6298f71855c03e", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.1" + }, + "time": "2024-08-21T13:31:24+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.5.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", + "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-04-18T09:32:20+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.30.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "0424dff1c58f028c451efff2045f5d92410bd540" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/0424dff1c58f028c451efff2045f5d92410bd540", + "reference": "0424dff1c58f028c451efff2045f5d92410bd540", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.30.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-05-31T15:07:36+00:00" + }, + { + "name": "symfony/yaml", + "version": "v6.4.11", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "be37e7f13195e05ab84ca5269365591edd240335" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/be37e7f13195e05ab84ca5269365591edd240335", + "reference": "be37e7f13195e05ab84ca5269365591edd240335", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "symfony/console": "<5.4" + }, + "require-dev": { + "symfony/console": "^5.4|^6.0|^7.0" + }, + "bin": [ + "Resources/bin/yaml-lint" + ], + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Loads and dumps YAML files", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/yaml/tree/v6.4.11" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-08-12T09:55:28+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [], + "plugin-api-version": "2.6.0" +} diff --git a/public/css/main.css b/public/css/main.css new file mode 100644 index 0000000..8dbe386 --- /dev/null +++ b/public/css/main.css @@ -0,0 +1,128 @@ +body { + font-family: 'Roboto', sans-serif; + padding-top: 50px; + background: linear-gradient(135deg, #f0f4f8, #e0e7ff); + transition: background 0.3s ease; + min-height: 100vh; +} + +.dark-mode { + background: linear-gradient(135deg, #0f0c29, #302b63, #24243e); +} + +.container { + max-width: 720px; + background-color: #fff; + padding: 30px; + border-radius: 10px; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); + transition: background-color 0.3s ease; +} + +.dark-mode .container { + background-color: #1e1e1e; + box-shadow: none; +} + +h1 { + font-size: 2rem; + font-weight: 500; + color: #424242; + text-align: center; + transition: color 0.3s ease; +} + +.dark-mode h1 { + color: #e0e0e0; +} + +label { + font-size: 1.1rem; + color: #424242; + transition: color 0.3s ease; +} + +.dark-mode label { + color: #e0e0e0; +} + +textarea, select { + font-size: 1rem; + padding: 10px; + border: 1px solid #d1d9e6; + background-color: #fff; + color: #333; + transition: background-color 0.3s ease, color 0.3s ease; +} + +.dark-mode textarea, .dark-mode select { + background-color: #2c2c2c; + color: #e0e0e0; + border: 1px solid #555; +} + +button { + font-size: 1.2rem; + padding: 12px; + background-color: #6200ea; + border: none; + border-radius: 5px; + color: white; + transition: background-color 0.3s ease; +} + +button:hover { + background-color: #3700b3; +} + +button:disabled { + background-color: #bbb; + cursor: not-allowed; +} + +.spinner-border { + display: none; + width: 1rem; + height: 1rem; +} + +.loading .spinner-border { + display: inline-block; +} + +.output-container { + background-color: #f0f4f8; + padding: 15px; + border-radius: 5px; + border: 1px solid #d1d9e6; + margin-top: 20px; + transition: background-color 0.3s ease, border-color 0.3s ease; +} + +.dark-mode .output-container { + background-color: #2c2c2c; + border-color: #555; +} + +.text-success { + color: #2e7d32; +} + +.text-danger { + color: #c62828; +} + +.switch-container { + display: flex; + justify-content: flex-end; + margin-top: -10px; + margin-bottom: 10px; +} + +.dark-switch { + display: flex; + justify-content: center; + align-items: center; + margin-top: 10px; + max-width: 50px; +} diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000..7c5d51a --- /dev/null +++ b/public/index.html @@ -0,0 +1,73 @@ + + + + + + + Serial2Format + + + + + + + +
+
+
+ +
+
+ +

Serial2Format

+ +
+ + +
+ +
+
+ + +
+
+ + +
+
+ +
+ +
+ +
+ + +
+ +
+ {{ message }} +
+
+ + + + + + diff --git a/public/index.php b/public/index.php new file mode 100644 index 0000000..b315af4 --- /dev/null +++ b/public/index.php @@ -0,0 +1,94 @@ +pushHandler(new StreamHandler(__DIR__ . '/../logs/app.log', Logger::DEBUG)); + +$converter = new FormatConverter($logger); + +// Forwardeo al frontend +if ($_SERVER['REQUEST_URI'] === '/') { + header('Location: /index.html'); + exit; +} + +/** + * Maneja la conversión de formatos. + */ +function handleConversion(FormatConverter $converter) +{ + if ($_SERVER['REQUEST_METHOD'] !== 'POST') { + sendErrorResponse(405, 'Método no permitido.'); + return; + } + + $inputData = file_get_contents('php://input'); + $requestData = json_decode($inputData, true); + + // Validar datos de entrada + if (json_last_error() !== JSON_ERROR_NONE || empty($requestData['inputData']) || empty($requestData['inputFormat']) || empty($requestData['outputFormat'])) { + sendErrorResponse(400, 'Solicitud no es válida. Asegúrese de que los datos están correctamente formateados.'); + return; + } + + $inputData = $requestData['inputData']; + $inputFormat = $requestData['inputFormat']; + $outputFormat = $requestData['outputFormat']; + + if ($inputFormat === $outputFormat) { + sendErrorResponse(400, 'Los formatos de entrada y salida no pueden ser iguales.'); + return; + } + + try { + $convertedData = $converter->convert($inputFormat, $outputFormat, $inputData); + + if ($convertedData === null) { + sendErrorResponse(500, 'Error interno en el servidor.'); + } else { + sendSuccessResponse(['convertedData' => $convertedData]); + } + } catch (Exception $e) { + sendErrorResponse(500, 'Error en la conversión: ' . $e->getMessage()); + } +} + +/** + * Envía una respuesta JSON con éxito. + * + * @param array $data + */ +function sendSuccessResponse(array $data) +{ + header('Content-Type: application/json'); + echo json_encode([ + 'status' => 'success', + 'data' => $data + ]); + exit; +} + +/** + * Envía una respuesta JSON de error. + * + * @param int $statusCode + * @param string $message + */ +function sendErrorResponse(int $statusCode, string $message) +{ + http_response_code($statusCode); + header('Content-Type: application/json'); + echo json_encode([ + 'status' => 'error', + 'message' => $message + ]); + exit; +} + +handleConversion($converter); diff --git a/public/js/index.js b/public/js/index.js new file mode 100644 index 0000000..11c628d --- /dev/null +++ b/public/js/index.js @@ -0,0 +1,109 @@ +new Vue({ + el: '#app', + data() { + return { + inputData: '', + inputFormat: 'auto', + outputFormat: 'json', + outputData: '', + loading: false, + message: '', + success: false, + darkMode: false // para el modo oscuro + }; + }, + methods: { + // function para auto-detectar el formato de los datos de entrada + detectFormat(input) { + try { + JSON.parse(input); + return 'json'; + } catch (e) { + // No es JSON + } + + if (input.trim().startsWith('<') && input.trim().endsWith('>')) { + return 'xml'; + } + + if (/^a:\d+:\{.*\}$/.test(input)) { + return 'phpSerialized'; + } + + return 'unknown'; + }, + toggleDarkMode() { + this.darkMode = !this.darkMode; // Invertir el valor de darkMode + }, + async convertData() { + this.message = ''; + + if (!this.inputData.trim()) { + this.showAlert('Por favor, ingrese los datos de entrada.', false); + return; + } + + // Si el usuario seleccionó "auto", detectar el formato + if (this.inputFormat === 'auto') { + const detectedFormat = this.detectFormat(this.inputData); + if (detectedFormat === 'unknown') { + this.showAlert('No se pudo detectar el formato de entrada.', false); + return; + } + this.inputFormat = detectedFormat; + this.showAlert(`Formato detectado: ${detectedFormat.toUpperCase()}.`, true); + } + + if (this.inputFormat === this.outputFormat) { + this.showAlert('Seleccione formatos diferentes para la conversión.', false); + return; + } + + this.loading = true; + + try { + const response = await fetch('/index.php', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + inputData: this.inputData, + inputFormat: this.inputFormat, + outputFormat: this.outputFormat + }) + }); + + const result = await response.json(); + + if (response.ok) { + this.outputData = result.data.convertedData; + this.$nextTick(() => hljs.highlightElement(this.$refs.outputData)); + this.showAlert('Conversión exitosa.', true); + } else { + this.outputData = 'Error en la conversión.'; + this.showAlert(result.message || 'Error desconocido.', false); + } + } catch (error) { + console.error('Error:', error); + this.outputData = 'Error al convertir los datos.'; + this.showAlert('Ocurrió un error al convertir los datos.', false); + } finally { + this.loading = false; + } + }, + showAlert(message, success) { + this.message = message; + this.success = success; + } + }, + watch: { + darkMode(val) { + if (val) { + document.body.classList.add('dark-mode'); + } else { + document.body.classList.remove('dark-mode'); + } + } + } +}); diff --git a/src/Adapters/JsonAdapter.php b/src/Adapters/JsonAdapter.php new file mode 100644 index 0000000..3272e1f --- /dev/null +++ b/src/Adapters/JsonAdapter.php @@ -0,0 +1,18 @@ +'); + ArrayToXml::convert($data, $xml); + $rawXml = $xml->asXML(); + + try { + $dom = new \DOMDocument(); + $dom->preserveWhiteSpace = false; + $dom->formatOutput = true; + + $dom->loadXML($rawXml); + + // XML formateado + return $dom->saveXML(); + } catch (\Exception $e) { + return $rawXml; + } + } +} diff --git a/src/Converter/FormatConverter.php b/src/Converter/FormatConverter.php new file mode 100644 index 0000000..0c8e7ad --- /dev/null +++ b/src/Converter/FormatConverter.php @@ -0,0 +1,46 @@ +logger = $logger; + $this->adapters = [ + 'json' => new JsonAdapter(), + 'xml' => new XmlAdapter(), + 'phpSerialized' => new PhpSerializedAdapter() + ]; + } + + public function convert(string $inputFormat, string $outputFormat, string $data): ?string + { + try { + if (!isset($this->adapters[$inputFormat]) || !isset($this->adapters[$outputFormat])) { + throw new \InvalidArgumentException("Formatos no soportados."); + } + + /** @var FormatAdapterInterface $inputAdapter */ + $inputAdapter = $this->adapters[$inputFormat]; + /** @var FormatAdapterInterface $outputAdapter */ + $outputAdapter = $this->adapters[$outputFormat]; + + $transformedData = $inputAdapter->deserialize($data); + + return $outputAdapter->serialize($transformedData); + } catch (\Exception $e) { + $this->logger->error('Error en la conversion de formatos' , ['exception' => $e]); + return null; + } + } +} \ No newline at end of file diff --git a/src/Interfaces/FormatAdapterInterface.php b/src/Interfaces/FormatAdapterInterface.php new file mode 100644 index 0000000..576d04b --- /dev/null +++ b/src/Interfaces/FormatAdapterInterface.php @@ -0,0 +1,22 @@ + $value) + { + if (is_numeric($key)) { + $key = 'item'; + } + if (is_array($value) || is_object($value)) { + $subnode = $xml->addChild($key); + self::convert($value, $subnode); + } else { + $xml->addChild("$key", htmlspecialchars("$value")); + } + } + } +} \ No newline at end of file