如何在test.php脚本中引导Magento 2?


93

在magento 1中,我可以创建一个只需要实例化Mage_Core_Model_App该类的文件,然后可以添加“脏”代码以进行测试。
像这样的东西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.

然后,我可以test.php在浏览器中调用并查看我在做什么。

Magento 2我该如何做?


4
magento 2 cron如何工作?也许您可以使用相同的方法?
2014年

4
好主意,但是...的代码来自cron.php$app = $bootstrap->createApplication('Magento\Framework\App\Cron', ['parameters' => ['group::']]);。我应该创建自己的应用程序模型吗?
马里乌斯

1
编写单元测试
Kristof在Fooman,2014年

2
@Fooman。请随意将其作为答案,但请提供示例。我是单元测试的新手。
马里乌斯

Answers:


86

基于@Flyingmana的回答,我做了一些挖掘并提出了解决方案。它适合我。
首先是我的解决方案,然后是一些解释。
我已经在test.phpmagento实例的根目录中创建了一个名为的文件。

<?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);

然后,我在TestApp.php与此内容相同的位置创建了一个文件。

<?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;
    }

}

现在,我可以test.php在浏览器中调用,TestApp :: launch()中放置的所有内容都将被执行。

现在,这为什么起作用:bootstrap类中
的方法createApplication是最重要的部分。它创建一个应用程序类的实例。该方法createApplication期望\Magento\Framework\AppInterface包含2个方法的实现。
因此,我在TestApp实现该接口的过程中创建了自己的类。我总是使方法catchException返回,false因为我不希望我的应用处理异常。万一有问题,只需在屏幕上打印即可。
然后我实现了该方法launch。这个被称为\Magento\Framework\App\Bootstrap::runrun无论作为参数传递的应用程序是什么,此方法几乎都可以执行相同的操作。
唯一依赖于应用程序的是此行:

$response = $application->launch();

这意味着调用\Magento\Framework\App\Bootstrap::run将初始化Magento env(也许做一些其他疯狂的事情……我还没有检查所有事情),然后launch从应用程序中调用该方法。
这就是为什么您需要将所有脏代码放入该方法的原因。
然后\Magento\Framework\App\Bootstrap::run调用$response->sendResponse();其中$response有什么launch方法返回。
这就是为什么return $this->_response;需要。它只是返回一个空响应。

我使我的应用程序类扩展了,\Magento\Framework\App\Http因此我已经有了请求和响应参数(以及其他参数),但是您可以使您的类什么都不扩展。然后,您需要从\Magento\Framework\App\Http类中复制构造函数。如果需要,可以在构造函数中添加更多参数。


2
当然,TestApp该类可以在同一test.php文件中定义,但是我不想让它变得肮脏:)
Marius

我必须添加parent::launch();方法的第一行,launch()因为它给了我“未设置区域代码”错误
OSdave 2015年

@OSdave。当我测试时,没有它就可以了。最有可能在最新版本中有所更改。
马吕斯

@Marius,我已经通过安装服务器的快速安装程序安装了magento。而且应用程序中没有bootstrap.php
er.irfankhan11 '16

1
@Butterfly您无需将其包含在自定义控制器中。该文件在到达您的控制器之前已包含在index.php中。
马里斯

54

对于快速/简短/肮脏的测试,我使用了类似的方法:

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());

4
这可行。其他答案没有。
ahnbizcad'9

1
这会触发我的HTTP 500。
Max

在2.1.2中仍然有效。我不得不修改require路径tho
simonthesorcerer

不适用于我
Sarfaraj Sipai '18

20

基于@Marius的答案,我想到了这个。

它既可以通过命令行也可以通过浏览器工作,我觉得这很有用。

这是一个示例脚本,用于以编程方式删除类别。

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);

然后我像这样运行它 php scripts/delete-category.php


2
前端工作正常,如果我想访问管理代码,则显示错误访问或区域问题,您能告诉我如何调用管理区域吗
Pradeep Kumar 2015年

尝试打电话时,我得到:Magento\Framework\Exception\LocalizedException: Area code is not set。我该如何设置?我需要朋友。
最多

恐怕自编写此代码以来,我对M2的关注不多,恐怕框架中的更改可能使其无效或需要修改,对不起!
·罗杰斯

18

根据要求,提供了一个简短的示例,说明如何编写测试(无需将其放在文件夹扩展结构中)。all,这都是命令行,不能通过浏览器使用。

创建文件

dev/tests/unit/quicktest.php

<?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');

    }
}

然后从目录dev/tests/unit/运行phpunit quicktest.php将执行您的代码。由于文件dev/tests/unit/phpunit.xml.dist会自动加载并准备环境,因此所有这些都可以使用。

在很多情况下,您可能必须将输入提供给类的构造函数。请查看下面的现有测试,以dev/tests/unit/testsuite/获取有关外观的更多示例,包括模拟对象。


1
我要求一个“肮脏的”操场。您在这里给了一个干净的:)。有趣的主意。我会试一试。
马里斯(Marius)

7
我发现过去我创建test.php的时间可能也已经花在编写测试上了,它将持续带来好处。
克里斯托夫在Fooman,2014年

15

这是比挂接到测试系统更好的方法:使用Magento 2的命令行界面。

这确实意味着您必须将沙箱代码集成到实际模块中(或为此目的创建一个模块),但是无论如何您都应该这样做。

设置好模块添加命令非常简单。您只需要一个类,然后用DI进行注册。

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>');
    }
}

https://github.com/magento/magento2-samples/tree/master/sample-module-command派生的示例-有关包含此功能的完整模块,请参见此处。包含了一些平凡的例子

按照约定,您的命令类应始终位于中{module}/Console/Command,并以结尾Command.php

添加了这两段代码(并刷新了Magento缓存等)后,请在SSH:中按名称执行命令php bin/magento example:greeting

您可以在这种情况下使用依赖注入,因此可以在其中运行任何代码execute()

该界面基于Symfony的Console组件构建,因此您还可以完全访问所有广泛的功能,包括选项/参数表格和非常简单的进度条

如果在设置命令或选项时遇到任何问题,通常可以运行“ list”命令以更好地了解问题所在: php bin/magento list

请享用。


真好!Symfony的进度条可用于导出量较大的脚本。谢谢
urbansurfers 18-3-23

13

重要的部分是 \Magento\Framework\App\Bootstrap::create

但是由于该Bootstrap::init()方法是私有的,并且发生了许多重要的事情,因此需要公共方法来调用它。

那一方面是createApplication()and之后的run()方法,还有getDirList()and getObjectManager()方法,两者都不需要参数。

因此,不需要应用程序,缺点是未初始化错误处理程序。


6

可能是题外话,但我始终使用Magento 1中的Contacts索引控制器文件来测试内容(IndexAction方法)。就像进入example.com/contacts一样简单。您只需要确保不提交那些更改即可;)

我敢肯定,您可以在Magento 2中执行类似的操作。不必使用引导代码创建新文件。


1
天堂禁止您忘记或在生产中使用它!请不要修改核心代码。
Ryan Hoerr

@RyanH。不会发生 版本控制,自动构建,静态代码分析,对等代码审查,登台/用户接受测试/等。但是,是的,如果没有的话,它很有可能最终投入生产:P
Erfan

1
这对您来说很棒,但是大多数在这里找的人都不会拥有这种控件。总是总是教(做)正确的做事方式。
Ryan Hoerr

5

该答案是对上面Marius答案的略微修改

因为在Magento 2.1中Area code not set使用该代码时出现了错误。So the intension of this answer is to fix that error on Magento 2.1

要解决此错误,您需要做的是在区域中定义区域test.php file。(请参见下面的修改文件)。

<?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);

并且TestApp.php文件将保持不变。

<?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;
    }

}

这在2.1.6中对我也不起作用,我明白了Uncaught TypeError: Argument 2 passed to Magento\\Framework\\App\\Http::__construct() must be an instance of Magento\\Framework\\Event\\Manager, none given
游击队

5

您可以通过添加以下代码直接在magento根上定向脚本,并将包含引导程序。.[在magento根文件夹中创建test.php并包含以下代码]

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');

希望这会有所帮助。


2

您可以使用以下代码从Magento 2根目录运行直接脚本。在Magento 2根目录中创建一个新文件,并添加此代码,然后在该文件中添加脚本。

<?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');

1

这是我将Magento初始化引入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);

根据Magento文档,这是推荐的方法。 http://devdocs.magento.com/guides/v2.0/config-guide/bootstrap/magento-bootstrap.html

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.