¿Cómo puedo arrancar Magento 2 en un script test.php?

93

En magento 1 pude crear un archivo donde solo necesitaba instanciar la Mage_Core_Model_Appclase y luego pude agregar mi código "sucio" para propósitos de prueba.
Algo como esto test.php:

<?php
//some settings
error_reporting(E_ALL | E_STRICT); 
define('MAGENTO_ROOT', getcwd()); 
$mageFilename = MAGENTO_ROOT . '/app/Mage.php'; 
require_once $mageFilename; 
Mage::setIsDeveloperMode(true); 
ini_set('display_errors', 1); 
umask(0);
//instantiate the app model
Mage::app(); 
//my toy code in here.

Luego pude llamar test.phpal navegador y ver qué estoy haciendo.

¿Cómo puedo hacer lo mismo para Magento 2?

Marius
fuente
44
¿Cómo funciona el cron de magento 2? ¿Se puede utilizar el mismo enfoque?
Amasty
44
Buena idea, pero ... código de cron.php: $app = $bootstrap->createApplication('Magento\Framework\App\Cron', ['parameters' => ['group::']]);. ¿Debo crear mi propio modelo de aplicación?
Marius
1
escribir una prueba de unidad
Kristof en Fooman
2
@Fooman. Siéntase libre de escribir esto como respuesta, pero proporcione un ejemplo. Soy un poco nuevo en las pruebas unitarias.
Marius

Respuestas:

86

Basado en la respuesta de @ Flyingmana, investigué un poco y encontré una solución. Parece que funciona para mí.
Primero mi solución, luego algunas explicaciones.
He creado un archivo llamado test.phpen la raíz de mi instancia de magento.

<?php
require __DIR__ . '/app/bootstrap.php';
$bootstrap = \Magento\Framework\App\Bootstrap::create(BP, $_SERVER);
/** @var \Magento\Framework\App\Http $app */
$app = $bootstrap->createApplication('TestApp');
$bootstrap->run($app);

Luego creé un archivo llamado TestApp.phpen el mismo lugar con este contenido.

<?php
class TestApp
    extends \Magento\Framework\App\Http
    implements \Magento\Framework\AppInterface {
    public function launch()
    {
        //dirty code goes here. 
        //the example below just prints a class name
        echo get_class($this->_objectManager->create('\Magento\Catalog\Model\Category'));
        //the method must end with this line
        return $this->_response;
    }

    public function catchException(\Magento\Framework\App\Bootstrap $bootstrap, \Exception $exception)
    {
        return false;
    }

}

Ahora solo puedo llamar test.phpal navegador y todo lo que se coloca en TestApp :: launch () se ejecutará.

Ahora, por qué esto funciona:
el método createApplicationde la clase bootstrap es la parte más importante. Crea una instancia de una clase de aplicación. El método createApplicationespera una implementación del \Magento\Framework\AppInterfaceque contiene 2 métodos.
Así que creé mi propia clase en TestAppque implementa esa interfaz. Siempre hice que el método catchExceptionregresara falseporque no quiero que mi aplicación maneje excepciones. En caso de que algo esté mal, simplemente imprímalo en la pantalla.
Luego implementé el método launch. Este es llamado por \Magento\Framework\App\Bootstrap::run. Este runmétodo hace casi lo mismo sin importar qué pase la aplicación como parámetro.
Lo único que depende de la aplicación es esta línea:

$response = $application->launch();

Esto significa que la llamada \Magento\Framework\App\Bootstrap::runiniciará el entorno de Magento (tal vez haga algunas otras cosas locas ... aún no he verificado todo) y luego llama al launchmétodo desde la aplicación.
Es por eso que necesitas poner todo tu código sucio dentro de ese método.
Luego las \Magento\Framework\App\Bootstrap::runllamadas $response->sendResponse();donde $responsees lo launchque devuelve el método.
Por eso return $this->_response;es necesario. Simplemente devuelve una respuesta vacía.

Hice que mi clase de aplicación se extendiera, \Magento\Framework\App\Httppor lo que ya tendré parámetros de solicitud y respuesta (y otros), pero puede hacer que su clase no extienda nada. Entonces necesitas copiar el constructor de la \Magento\Framework\App\Httpclase. Tal vez agregue más parámetros en el constructor si lo necesita.

Marius
fuente
2
Por supuesto, la TestAppclase podría haberse definido en el mismo test.phparchivo, pero no quiero ensuciarla :)
Marius
Tuve que agregar parent::launch();como la primera línea de launch()método, ya que me estaba dando un error "Código de área no establecido"
OSdave
@OSdave. Funcionó sin eso cuando lo probé. Lo más probable es que algo haya cambiado en las últimas versiones.
Marius
@Marius, instalé magento al instalar la instalación rápida del servidor. Y no tengo bootstrap.php en la aplicación
er.irfankhan11
1
@Butterfly No necesita incluirlo en su controlador personalizado. El archivo se incluye en index.php antes de que llegue a su controlador.
Marius
54

Para pruebas rápidas / cortas / sucias, usé algo como esto:

use Magento\Framework\App\Bootstrap;
require __DIR__ . '/../app/bootstrap.php';

$bootstrap = Bootstrap::create(BP, $_SERVER);

$obj = $bootstrap->getObjectManager();

$state = $obj->get('Magento\Framework\App\State');
$state->setAreaCode('frontend');

$quote = $obj->get('Magento\Checkout\Model\Session')->getQuote()->load(1);
print_r($quote->getOrigData());
carco
fuente
44
esto funciona. las otras respuestas no.
ahnbizcad
1
Esto desencadena HTTP 500 a mi lado.
Max
Todavía funciona en 2.1.2. Tuve que modificar la ruta requerida aunque
simonthesorcerer
no funcionó para mí
Sarfaraj Sipai
20

Basado en la respuesta de @ Marius se me ocurrió esto.

Funciona tanto a través de la línea de comandos como del navegador, lo que me parece útil.

Aquí hay una secuencia de comandos de muestra para eliminar la categoría mediante programación.

scripts/abstract.php

<?php
use \Magento\Framework\AppInterface as AppInterface;
use \Magento\Framework\App\Http as Http;

use Magento\Framework\ObjectManager\ConfigLoaderInterface;
use Magento\Framework\App\Request\Http as RequestHttp;
use Magento\Framework\App\Response\Http as ResponseHttp;
use Magento\Framework\Event;
use Magento\Framework\Filesystem;
use Magento\Framework\App\AreaList as AreaList;
use Magento\Framework\App\State as State;

abstract class AbstractApp implements AppInterface
{
    public function __construct(
        \Magento\Framework\ObjectManagerInterface $objectManager,
        Event\Manager $eventManager,
        AreaList $areaList,
        RequestHttp $request,
        ResponseHttp $response,
        ConfigLoaderInterface $configLoader,
        State $state,
        Filesystem $filesystem,
        \Magento\Framework\Registry $registry
    ) {
        $this->_objectManager = $objectManager;
        $this->_eventManager = $eventManager;
        $this->_areaList = $areaList;
        $this->_request = $request;
        $this->_response = $response;
        $this->_configLoader = $configLoader;
        $this->_state = $state;
        $this->_filesystem = $filesystem;
        $this->registry = $registry;
    }

    public function launch()
    {
        $this->run();
        return $this->_response;
    }

    abstract public function run();

    public function catchException(\Magento\Framework\App\Bootstrap $bootstrap, \Exception $exception)
    {
        return false;
    }
}

scripts/delete-category.php

<?php
require dirname(__FILE__) . '/../app/bootstrap.php';
$bootstrap = \Magento\Framework\App\Bootstrap::create(BP, $_SERVER);
require dirname(__FILE__) . '/abstract.php';

class CreateCategoriesApp extends AbstractApp
{

    public function run()
    {
        $this->_objectManager->get('Magento\Framework\Registry')
            ->register('isSecureArea', true);

        $category = $this->_objectManager->create('\Magento\Catalog\Model\Category');
        $category = $category->load(343);

        $category->delete();
    }
}

/** @var \Magento\Framework\App\Http $app */
$app = $bootstrap->createApplication('CreateCategoriesApp');
$bootstrap->run($app);

Entonces lo corro como php scripts/delete-category.php

Luke Rodgers
fuente
2
funciona bien para frontend, si quiero acceder al código de administrador, entonces muestra un error de acceso o un problema de área, ¿puede decir cómo llamar al área de administración
Pradeep Kumar
Cuando se trata de llamar a algo, me sale: Magento\Framework\Exception\LocalizedException: Area code is not set. ¿Cómo puedo configurarlo? Necesito el amigo
Max
No he mirado mucho a M2 desde que escribí este código, me temo, los cambios en el marco pueden haberlo hecho inválido o necesitar una modificación, lo siento
Luke Rodgers
18

Según lo solicitado, un ejemplo muy breve de cómo podría escribir una prueba (sin colocarla en la estructura de extensión de su carpeta). Por desgracia, esto es todo línea de comandos y no para el consumo a través de un navegador.

Crea el archivo

dev/tests/unit/quicktest.php

con

<?php

class QuickTest extends \PHPUnit_Framework_TestCase
{
    public function testExample()
    {
        //instantiate your class
        $context = new Magento\Framework\Object();

        $context->setData('param', 'value');

        //test whatever you want to test
        $this->assertEquals('value', $context->getData('param'));

        //you could even output to console
        echo $context->getData('param');

    }
}

luego desde el directorio dev/tests/unit/ejecutado phpunit quicktest.phpque ejecutará su código. Todo esto funciona ya que el archivo dev/tests/unit/phpunit.xml.distse carga automáticamente y prepara el entorno.

En muchos casos, es posible que deba proporcionar información al constructor de las clases. Consulte las pruebas existentes dev/tests/unit/testsuite/a continuación para obtener más ejemplos de cómo podría verse esto, incluidos los objetos de burla.

Kristof en Fooman
fuente
1
Pedí un patio de juegos "sucio". Diste aquí uno limpio :). Idea interesante. Voy a darle una oportunidad.
Marius
77
Creo que las veces que hubiera creado un test.php en el pasado, el esfuerzo bien podría haber sido escribir un examen que tendrá un beneficio continuo.
Kristof en Fooman el
15

Aquí hay una mejor manera que conectarse al sistema de prueba: use la interfaz de línea de comandos de Magento 2.

Esto significa que tendrá que integrar su código de sandbox en un módulo real (o crear uno para el propósito), pero debería hacerlo de todos modos.

Una vez que haya configurado su módulo , agregar un comando es bastante fácil. Todo lo que necesita es la clase y DI para registrarla.

1. {module} /etc/di.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="Magento\Framework\Console\CommandList">
        <arguments>
            <argument name="commands" xsi:type="array">
                <item name="greeting_command" xsi:type="object">Magento\CommandExample\Console\Command\GreetingCommand</item>
            </argument>
        </arguments>
    </type>
</config>

2. {module} /Console/Command/GreetingCommand.php

<?php

namespace Magento\CommandExample\Console\Command;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

/**
 * Class GreetingCommand
 */
class GreetingCommand extends Command
{
    /**
     * {@inheritdoc}
     */
    protected function configure()
    {
        $this->setName('example:greeting')
             ->setDescription('Greeting command');

        parent::configure();
    }

    /**
     * {@inheritdoc}
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $output->writeln('<info>Hello world!</info>');
    }
}

Ejemplo derivado de https://github.com/magento/magento2-samples/tree/master/sample-module-command ; consulte allí un módulo completo que incorpora esta funcionalidad. Hay ejemplos menos triviales incluidos.

Por convención, su clase de comando siempre debe estar en {module}/Console/Commandy terminar con Command.php.

Una vez añadidos los dos trozos de código (y la memoria caché Magento enrojecida, etc.), ejecute el comando por su nombre en SSH: php bin/magento example:greeting.

Puede usar la inyección de dependencia en este contexto, por lo que puede ejecutar cualquier código que desee dentro execute().

Esta interfaz se basa en el componente Consola de Symfony , por lo que también tiene acceso completo a toda esa amplia gama de funcionalidades, incluidas opciones / argumentos , tablas y barras de progreso muy fáciles .

Si tiene algún problema al configurar su comando u opciones, generalmente puede ejecutar el comando 'lista' para obtener una mejor visibilidad de lo que está mal: php bin/magento list

Disfrutar.

Ryan Hoerr
fuente
¡Agradable! con las barras de progreso de Symfony para scripts con gran exportación. gracias
urbansurfers
13

La parte importante es la \Magento\Framework\App\Bootstrap::create

pero como el Bootstrap::init()método es privado y ocurren muchas cosas importantes, se necesitan métodos públicos que lo llamen.

Eso es, por un lado, createApplication()y seguir el run()método, pero también el método getDirList()y getObjectManager(), que no necesitan argumento.

Por lo tanto, no se necesita una aplicación, las desventajas son que el controlador de errores no se inicializa.

Flyingmana
fuente
6

Posiblemente fuera de tema, pero siempre uso el archivo del controlador de índice de Contactos en Magento 1 para probar cosas (método IndexAction). Es tan simple como ir a example.com/contacts. Solo tiene que asegurarse de no cometer esos cambios;)

Estoy seguro de que puede hacer algo similar en Magento 2. Le ahorra tener que crear un nuevo archivo con el código de arranque.

Erfan
fuente
1
¡El cielo te prohíba que te olvides, o que lo hagas en producción! Por favor no modifique el código central.
Ryan Hoerr
@RyanH. No va a suceder Control de versiones, compilaciones automatizadas, análisis de código estático, revisión de código de pares, puesta en escena / pruebas de aceptación del usuario / etc. Pero sí, si no tienes eso, existe la posibilidad de que termine en producción: P
Erfan
1
Eso es genial para ti, pero la mayoría de las personas que buscan aquí no tendrán ese tipo de controles. Es mejor enseñar (y hacer) la forma correcta de hacer las cosas, siempre.
Ryan Hoerr
5

Esta respuesta es una ligera modificación a la respuesta anterior de Marius

Porque en Magento 2.1 obtuve el error como Area code not setal usar ese código.So the intension of this answer is to fix that error on Magento 2.1

Lo que debe hacer para corregir este error es definir el área en su test.php file. (Ver el archivo modificado a continuación).

<?php
require __DIR__ . '/app/bootstrap.php';
$bootstrap = \Magento\Framework\App\Bootstrap::create(BP, $_SERVER);
$obj = $bootstrap->getObjectManager();

$state = $obj->get('Magento\Framework\App\State');
$state->setAreaCode('frontend');
/** @var \Magento\Framework\App\Http $app */
$app = $bootstrap->createApplication('TestApp');
$bootstrap->run($app);

Y el TestApp.phparchivo seguirá siendo el mismo.

<?php

class TestApp
    extends \Magento\Framework\App\Http
    implements \Magento\Framework\AppInterface {
    public function launch()
    {
        //dirty code goes here.
        $objectManager = \Magento\Framework\App\ObjectManager::getInstance();
        $product = $objectManager->get('Magento\Catalog\Model\Product')->load(71);
        var_dump($product->getData());

        return $this->_response;
    }

    public function catchException(\Magento\Framework\App\Bootstrap $bootstrap, \Exception $exception)
    {
        return false;
    }

}
Sukeshini
fuente
Esto tampoco me funciona en 2.1.6, obtengoUncaught TypeError: Argument 2 passed to Magento\\Framework\\App\\Http::__construct() must be an instance of Magento\\Framework\\Event\\Manager, none given
Guerrilla
5

Puede dirigir el script en la raíz de magento agregando el siguiente código y se incluirá bootstrap ... [Cree test.php en la carpeta raíz de magento e incluya el código a continuación]

ini_set('display_errors', 1);
ini_set('max_execution_time', 0);
ini_set("memory_limit", "-1");
set_time_limit(0);
error_reporting(E_ALL);
require './app/bootstrap.php';
$bootstrap = \Magento\Framework\App\Bootstrap::create(BP, $_SERVER);
$objectManager = $bootstrap->getObjectManager();
$state = $objectManager->get('Magento\Framework\App\State');
$state->setAreaCode('admin');

Esperamos que esto sea útil.

Yogesh
fuente
2

Puede ejecutar el script directo desde la raíz de Magento 2 usando el código a continuación. Cree un nuevo archivo en el directorio raíz de Magento 2 y agregue este código y luego agregue su script en el archivo.

<?php
    use Magento\Framework\App\Bootstrap;
    include('app/bootstrap.php');
    $bootstrap = Bootstrap::create(BP, $_SERVER);

    $objectManager = $bootstrap->getObjectManager();

    $state = $objectManager->get('Magento\Framework\App\State');
    $state->setAreaCode('frontend');
Evince Development
fuente
1

Esto es lo que hice para inicializar Magento en mi script personalizado fuera del directorio de magento.

//Required to include Magento functions.
$magento_dir "your/path/to/the/magento/installation/directory/";
require $magento_dir . 'app/bootstrap.php';
$bootstrap = \Magento\Framework\App\Bootstrap::create(BP, $_SERVER);
//$app = $bootstrap->createApplication('Magento\Framework\App\Http');
$app = $bootstrap->createApplication('MyClass');
$bootstrap->run($app);

Esta es la forma recomendada según los documentos de Magento. http://devdocs.magento.com/guides/v2.0/config-guide/bootstrap/magento-bootstrap.html

MagentoMan
fuente