- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
为了更好地管理我如何使用 Doctrine 进行数据库调用,我研究了如何将 php debug_backtrace 添加到 symfony doctrine 数据库收集器。
我设法确定了我必须进行的修改,但我还没有找到覆盖 DebugStack 类的好方法(除了覆盖供应商类之外)。
类(class)位于此处:
vendor/doctrine/dbal/lib/Doctrine/DBAL/Logging/DebugStack.php
对于那些感兴趣的人,这是结果:
为此,您需要:
覆盖 vendor/doctrine/doctrine-bundle/Resources/views/Collector/db.html.twig
文件
将此文件复制到 app\resources\DoctrineBundle\views\Collector\db.html.twig
你会在两条====MODIFIED===评论下面看到我的修改:
{% extends app.request.isXmlHttpRequest ? 'WebProfilerBundle:Profiler:ajax_layout.html.twig' : 'WebProfilerBundle:Profiler:layout.html.twig' %}
{% block toolbar %}
{% set icon %}
<img width="20" height="28" alt="Database" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAcCAYAAABh2p9gAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQRJREFUeNpi/P//PwM1ARMDlcGogZQDlpMnT7pxc3NbA9nhQKxOpL5rQLwJiPeBsI6Ozl+YBOOOHTv+AOllQNwtLS39F2owKYZ/gRq8G4i3ggxEToggWzvc3d2Pk+1lNL4fFAs6ODi8JzdS7mMRVyDVoAMHDsANdAPiOCC+jCQvQKqBQB/BDbwBxK5AHA3E/kB8nKJkA8TMQBwLxaBIKQbi70AvTADSBiSadwFXpCikpKQU8PDwkGTaly9fHFigkaKIJid4584dkiMFFI6jkTJII0WVmpHCAixZQEXWYhDeuXMnyLsVlEQKI45qFBQZ8eRECi4DBaAlDqle/8A48ip6gAADANdQY88Uc0oGAAAAAElFTkSuQmCC" />
<span class="sf-toolbar-status{% if 50 < collector.querycount %} sf-toolbar-status-yellow{% endif %}">{{ collector.querycount }}</span>
{% if collector.querycount > 0 %}
<span class="sf-toolbar-info-piece-additional-detail">in {{ '%0.2f'|format(collector.time * 1000) }} ms</span>
{% endif %}
{% if collector.invalidEntityCount > 0 %}
<span class="sf-toolbar-info-piece-additional sf-toolbar-status sf-toolbar-status-red">{{ collector.invalidEntityCount }}</span>
{% endif %}
{% if collector.cacheEnabled %}
<span class="sf-toolbar-info-piece-additional sf-toolbar-status sf-toolbar-status-green" title="Second level cache enabled">2l cache</span>
{% endif %}
{% endset %}
{% set text %}
<div class="sf-toolbar-info-piece">
<b>DB Queries</b>
<span>{{ collector.querycount }}</span>
</div>
<div class="sf-toolbar-info-piece">
<b>Query time</b>
<span>{{ '%0.2f'|format(collector.time * 1000) }} ms</span>
</div>
<div class="sf-toolbar-info-piece">
<b>Invalid entities</b>
<span class="sf-toolbar-status sf-toolbar-status-{{ collector.invalidEntityCount > 0 ? 'red' : 'green' }}">{{ collector.invalidEntityCount }}</span>
</div>
{% if collector.cacheEnabled %}
<div class="sf-toolbar-info-piece">
<b>Cache hits</b>
<span class="sf-toolbar-status sf-toolbar-status-green">{{ collector.cacheHitsCount }}</span>
</div>
<div class="sf-toolbar-info-piece">
<b>Cache misses</b>
<span class="sf-toolbar-status sf-toolbar-status-{{ collector.cacheMissesCount > 0 ? 'yellow' : 'green' }}">{{ collector.cacheMissesCount }}</span>
</div>
<div class="sf-toolbar-info-piece">
<b>Cache puts</b>
<span class="sf-toolbar-status sf-toolbar-status-{{ collector.cachePutsCount > 0 ? 'yellow' : 'green' }}">{{ collector.cachePutsCount }}</span>
</div>
{% endif %}
{% endset %}
{% include 'WebProfilerBundle:Profiler:toolbar_item.html.twig' with { 'link': profiler_url } %}
{% endblock %}
{% block menu %}
<span class="label">
<span class="icon"><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAcCAYAAAB/E6/TAAABLUlEQVR42u3TP0vDQBiA8UK/gDiLzi0IhU4OEunk5OQUAhGSOBUCzqWfIKSzX8DRySF0URCcMjWLIJjFD9Cpk/D6HITecEPUuzhIAz8CIdyTP/f2iqI4qaqqDx8l5Ic2uIeP/bquezCokOAFF+oCN3t4gPzSEjc4NEPaCldQbzjELTYW0RJzHDchwwem+ons6ZBpLSJ7nueJC22h0V+FzmwWV0ee59vQNV67CGVZJmEYbkNjfpY6X6I0Qo4/3RMmTdDDspuQVsJvgkP3IdMbIkIjLPBoadG2646iKJI0Ta2wxm6OdnP0/Tk6DYJgHcfxpw21RtscDTDDnaVZ26474GkkSRIrrPEv5sgMTfHe+cA2O6wPH6vOBpYQNALneHb96XTEDI6dzpEZ0VzO0Rf3pP5LMLI4tAAAAABJRU5ErkJggg==" alt="" /></span>
<strong>Doctrine</strong>
<span class="count">
<span>{{ collector.querycount }}</span>
<span>{{ '%0.0f'|format(collector.time * 1000) }} ms</span>
</span>
</span>
{% endblock %}
{% block panel %}
{% if 'explain' == page %}
{{ render(controller('DoctrineBundle:Profiler:explain', {
'token': token,
'panel': 'db',
'connectionName': app.request.query.get('connection'),
'query': app.request.query.get('query')
})) }}
{% else %}
{{ block('queries') }}
{% endif %}
{% endblock %}
{% block queries %}
<h2>Queries</h2>
{# ========================================= MODIFIED ===============================#}
<script src="{{ asset('vendor/jquery/jquery-2.1.1.min.js') }}"></script>
<style rel="stylesheet">
.hidden {display: none;}
</style>
<script>
$(function(){
$('.trace').on('click', function(e){
e.preventDefault();
$(this).next().toggleClass('hidden');
});
});
</script>
{% for connection, queries in collector.queries %}
<h3>Connection <em>{{ connection }}</em></h3>
{% if queries is empty %}
<p>
<em>No queries.</em>
</p>
{% else %}
<p>
<button type="button" class="sf-button" onclick="expandAllQueries(this);" data-action="expand">
<span class="border-l">
<span class="border-r">
<span class="btn-bg">Expand all queries</span>
</span>
</span>
</button>
</p>
<table class="alt" id="queriesPlaceholder-{{ loop.index }}">
<thead>
<tr>
<th onclick="javascript:sortTable(this, 0, 'queries-{{ loop.index }}')" data-sort-direction="-1" style="cursor: pointer;">#<span>▲</span></th>
<th onclick="javascript:sortTable(this, 1, 'queries-{{ loop.index }}')" style="cursor: pointer;">Time<span></span></th>
<th style="width: 100%;">Info</th>
</tr>
</thead>
<tbody id="queries-{{ loop.index }}">
{% for i, query in queries %}
<tr id="queryNo-{{ i }}-{{ loop.parent.loop.index }}" class="{{ cycle(['odd', 'even'], i) }}">
<td>{{ loop.index }}</td>
<td>{{ '%0.2f'|format(query.executionMS * 1000) }} ms</td>
<td>
<div class="query-section" data-state="collapsed" onclick="return expandQuery(this);" title="Expand query" data-target-id="code-{{ i }}-{{ loop.parent.loop.index }}" style="cursor: pointer;">
<img alt="+" src="data:image/gif;base64,R0lGODlhEgASAMQTANft99/v+Ga44bHb8ITG52S44dXs9+z1+uPx+YvK6WC24G+944/M6W28443L6dnu+Ge54v/+/l614P///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAABMALAAAAAASABIAQAVS4DQBTiOd6LkwgJgeUSzHSDoNaZ4PU6FLgYBA5/vFID/DbylRGiNIZu74I0h1hNsVxbNuUV4d9SsZM2EzWe1qThVzwWFOAFCQFa1RQq6DJB4iIQA7" style="display: inline;" />
<img alt="-" src="data:image/gif;base64,R0lGODlhEgASAMQSANft94TG57Hb8GS44ez1+mC24IvK6ePx+Wa44dXs92+942e54o3L6W2844/M6dnu+P/+/l614P///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAABIALAAAAAASABIAQAVCoCQBTBOd6Kk4gJhGBCTPxysJb44K0qD/ER/wlxjmisZkMqBEBW5NHrMZmVKvv9hMVsO+hE0EoNAstEYGxG9heIhCADs=" style="display: none;" />
<span style="display: none">Shrink query</span>
<span id="smallcode-{{ i }}-{{ loop.parent.loop.index }}">
{{ query.sql|doctrine_minify_query|raw }}
</span>
</div>
<code id="code-{{ i }}-{{ loop.parent.loop.index }}">
{{ query.sql|doctrine_pretty_query(i, loop.parent.loop.index)|raw }}
</code>
<span id="original-query-{{ i }}-{{ loop.parent.loop.index }}" style="display: none;">
{{ query.sql|doctrine_replace_query_parameters(query.params)|raw }}
</span>
<small>
<strong>Parameters</strong>: {{ query.params|yaml_encode }} <br />
[<span id="expandParams-{{ i }}-{{ loop.parent.loop.index }}" onclick="javascript:toggleRunnableQuery(this);" target-data-id="original-query-{{ i }}-{{ loop.parent.loop.index }}" style="cursor: pointer;">Display runnable query</span>]<br/>
</small>
{% if query.explainable %}
[<a href="{{ path('_profiler', {'panel': 'db', 'token': token, 'page': 'explain', 'connection': connection, 'query': i}) }}" onclick="return explain(this);" style="text-decoration: none;" title="Explains the query" data-target-id="explain-{{ i }}-{{ loop.parent.loop.index }}" >
<img alt="+" src="data:image/gif;base64,R0lGODlhEgASAMQTANft99/v+Ga44bHb8ITG52S44dXs9+z1+uPx+YvK6WC24G+944/M6W28443L6dnu+Ge54v/+/l614P///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAABMALAAAAAASABIAQAVS4DQBTiOd6LkwgJgeUSzHSDoNaZ4PU6FLgYBA5/vFID/DbylRGiNIZu74I0h1hNsVxbNuUV4d9SsZM2EzWe1qThVzwWFOAFCQFa1RQq6DJB4iIQA7" style="display: inline; width: 12px; height: 12px;" />
<img alt="-" src="data:image/gif;base64,R0lGODlhEgASAMQSANft94TG57Hb8GS44ez1+mC24IvK6ePx+Wa44dXs92+942e54o3L6W2844/M6dnu+P/+/l614P///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAABIALAAAAAASABIAQAVCoCQBTBOd6Kk4gJhGBCTPxysJb44K0qD/ER/wlxjmisZkMqBEBW5NHrMZmVKvv9hMVsO+hE0EoNAstEYGxG9heIhCADs=" style="display: none; width: 12px; height: 12px;" />
<span style="vertical-align:top">Explain query</span>
</a>]
{% else %}
This query cannot be explained
{% endif %}
{% if query.explainable %}
<div id="explain-{{ i }}-{{ loop.parent.loop.index }}" class="loading"></div>
{% endif %}
{# ========================================= MODIFIED ===============================#}
{% if query.trace is defined %}
<div>
[<a href="#/" style="text-decoration: none;" title="Show the trace" class="trace">
<img alt="+" src="data:image/gif;base64,R0lGODlhEgASAMQTANft99/v+Ga44bHb8ITG52S44dXs9+z1+uPx+YvK6WC24G+944/M6W28443L6dnu+Ge54v/+/l614P///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAABMALAAAAAASABIAQAVS4DQBTiOd6LkwgJgeUSzHSDoNaZ4PU6FLgYBA5/vFID/DbylRGiNIZu74I0h1hNsVxbNuUV4d9SsZM2EzWe1qThVzwWFOAFCQFa1RQq6DJB4iIQA7" style="display: inline; width: 12px; height: 12px;" />
<span style="vertical-align:top">Show the trace</span>
</a>]
<div class="hidden">{{ dump(query.trace) }}</div>
</div>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
{% endfor %}
<h2>Database Connections</h2>
{% if collector.connections %}
{% include 'WebProfilerBundle:Profiler:table.html.twig' with {data: collector.connections} only %}
{% else %}
<p>
<em>No connections.</em>
</p>
{% endif %}
<h2>Entity Managers</h2>
{% if collector.managers %}
{% include 'WebProfilerBundle:Profiler:table.html.twig' with {data: collector.managers} only %}
{% else %}
<p>
<em>No entity managers.</em>
</p>
{% endif %}
<h2>Second Level Cache</h2>
{% if collector.cacheCounts %}
{% include 'WebProfilerBundle:Profiler:table.html.twig' with {data: collector.cacheCounts} only %}
{% if collector.cacheRegions.hits %}
<h3>Number of cache hits</h3>
{% include 'WebProfilerBundle:Profiler:table.html.twig' with {data: collector.cacheRegions.hits} only %}
{% endif %}
{% if collector.cacheRegions.misses %}
<h3>Number of cache misses</h3>
{% include 'WebProfilerBundle:Profiler:table.html.twig' with {data: collector.cacheRegions.misses} only %}
{% endif %}
{% if collector.cacheRegions.puts %}
<h3>Number of cache puts</h3>
{% include 'WebProfilerBundle:Profiler:table.html.twig' with {data: collector.cacheRegions.puts} only %}
{% endif %}
{% else %}
<p>
<em>No cache.</em>
</p>
{% endif %}
<h2>Mapping</h2>
{% for manager, classes in collector.entities %}
<h3>Manager <em>{{ manager }}</em></h3>
{% if classes is empty %}
<p><em>No loaded entities.</em></p>
{% else %}
<table>
<thead>
<tr>
<th scope="col">Class</th>
<th scope="col">Mapping errors</th>
</tr>
</thead>
<tbody>
{% for class in classes %}
<tr>
<td>{{ class }}</td>
<td>
{% if collector.mappingErrors[manager][class] is defined %}
<ul>
{% for error in collector.mappingErrors[manager][class] %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% else %}
Valid
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
{% endfor %}
<script type="text/javascript">//<![CDATA[
function explain(link) {
"use strict";
var imgs = link.children,
target = link.getAttribute('data-target-id');
Sfjs.toggle(target, imgs[0], imgs[1])
.load(
target,
link.href,
null,
function(xhr, el) {
el.innerHTML = 'An error occurred while loading the details';
Sfjs.removeClass(el, 'loading');
}
);
return false;
}
function expandAllQueries(button) {
var queries = document.getElementsByClassName('query-section'),
i = queries.length,
action = button.getAttribute('data-action');
if (action == 'expand') {
button.getElementsByClassName('btn-bg')[0].innerHTML = 'Collapse all queries';
while (i--) {
if (queries[i].getAttribute('data-state') == 'collapsed') {
expandQuery(queries[i]);
}
}
} else {
button.getElementsByClassName('btn-bg')[0].innerHTML = 'Expand all queries';
while (i--) {
if (queries[i].getAttribute('data-state') == 'expanded') {
expandQuery(queries[i]);
}
}
}
button.setAttribute('data-action', action == 'expand' ? 'collapse' : 'expand');
}
function expandQuery(link) {
var sections = link.children,
target = link.getAttribute('data-target-id'),
targetId = target.replace('code', ''),
queriesParameters = document.getElementById('original-query' + targetId);
if (queriesParameters.style.display != 'none') {
queriesParameters.style.display = 'none';
document.getElementById('small' + target).style.display = 'inline';
document.getElementById('expandParams' + targetId).innerHTML = 'Display runnable query';
}
if (document.getElementById('small' + target).style.display != 'none') {
document.getElementById('small' + target).style.display = 'none';
document.getElementById(target).style.display = 'inline';
sections[0].style.display = 'none';
sections[1].style.display = 'inline';
sections[2].style.display = 'inline';
link.setAttribute('data-state', 'expanded');
} else {
document.getElementById('small' + target).style.display = 'inline';
document.getElementById(target).style.display = 'none';
sections[0].style.display = 'inline';
sections[1].style.display = 'none';
sections[2].style.display = 'none';
link.setAttribute('data-state', 'collapsed');
}
return false;
}
function toggleRunnableQuery(target) {
var targetId = target.getAttribute('target-data-id').replace('original-query', ''),
targetElement = document.getElementById(target.getAttribute('target-data-id')),
elem;
if (targetElement.style.display != 'block') {
targetElement.style.display = 'block';
target.innerHTML = 'Hide runnable query';
document.getElementById('smallcode' + targetId).style.display = 'none';
document.getElementById('code' + targetId).style.display = 'none';
elem = document.getElementById('code' + targetId).parentElement.children[0];
elem.children[0].style.display = 'inline';
elem.children[1].style.display = 'none';
elem.children[2].style.display = 'none';
} else {
targetElement.style.display = 'none';
target.innerHTML = 'Display runnable query';
document.getElementById('smallcode' + targetId).style.display = 'inline';
}
}
function sortTable(header, column, targetId) {
"use strict";
var direction = parseInt(header.getAttribute('data-sort-direction')) || 1,
items = [],
target = document.getElementById(targetId),
rows = target.children,
headers = header.parentElement.children,
i;
for (i = 0; i < rows.length; ++i) {
items.push(rows[i]);
}
for (i = 0; i < headers.length; ++i) {
headers[i].removeAttribute('data-sort-direction');
if (headers[i].children.length > 0) {
headers[i].children[0].innerHTML = '';
}
}
header.setAttribute('data-sort-direction', (-1*direction).toString());
header.children[0].innerHTML = direction > 0 ? '▲' : '▼';
items.sort(function(a, b) {
return direction*(parseFloat(a.children[column].innerHTML) - parseFloat(b.children[column].innerHTML));
});
for (i = 0; i < items.length; ++i) {
Sfjs.removeClass(items[i], i % 2 ? 'even' : 'odd');
Sfjs.addClass(items[i], i % 2 ? 'odd' : 'even');
target.appendChild(items[i]);
}
}
//]]></script>
<style>
h3 {
margin-bottom: 0px;
}
code {
display: none;
}
code pre {
padding: 5px;
}
</style>
{% endblock %}
接下来编辑vendor/doctrine/dbal/lib/Doctrine/DBAL/Logging/DebugStack.php
文件
并按如下方式更改 startQuery 方法:
/**
* {@inheritdoc}
*/
public function startQuery($sql, array $params = null, array $types = null)
{
if ($this->enabled) {
$this->start = microtime(true);
$this->queries[++$this->currentQuery] = array('sql' => $sql, 'params' => $params, 'types' => $types, 'executionMS' => 0, 'trace' => debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS));
}
}
我还没有找到如何进行更有效的更改,所以这里的每一个提议都会非常好!谢谢!
最佳答案
所有 DoctrineBundle 类都在这里配置:https://github.com/doctrine/DoctrineBundle/blob/master/Resources/config/dbal.xml
你可以像这样用你自己的实现覆盖“doctrine.dbal.logger.profiling.class”:
# app/config/config.yml
parameters:
doctrine.dbal.logger.profiling.class: Acme\HelloBundle\Logging\MyDebugStack
在你的类里面:
namespace Acme\HelloBundle\Logging;
use Doctrine\DBAL\Logging\DebugStack;
class MyDebugStack extends DebugStack
{
//...
}
此外,请阅读有关包覆盖的部分:http://symfony.com/doc/current/cookbook/bundles/override.html
关于php - Symfony2/Doctrine2 : How to override DebugStack class used in doctrine DataCollector?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29855412/
我开始从事一个用 Symfony 2.8 编写的大型项目。将整个项目升级到 SF 3 需要数百小时,现在还不可能。我想到了一个想法,将 symfony/symfony 包解压到它替换的单个包中(com
我在提交表单后使用 FOSUserEvents,但订阅者调用了两次。 这样我的验证码第一次有效第二次无效 这是我的代码 router = $router; $this->request
我有以下路线: blog_show: path: /test/123 defaults: { _controller: TotalcanBravofillBundle:Te
我是测试新手。我想测试我的功能。我已经成功安装了 phpUnit。我在互联网上查看了许多教程。但我无法获得有关测试的正确信息。这是我的功能代码: public function loginAction
我正在尝试重现 facebook batch requests 的行为在他们的图形 api 上运行。 所以我认为最简单的解决方案是在 Controller 上向我的应用程序发出几个请求,例如: pub
在 Symfony Progress Bar documentation有一个超酷酒吧的示例图像。不幸的是,看起来文档的其余部分没有解释如何获得这样的结果。 这是图片,以防您错过: 我怎么才能得到它?
我使用Finder发送假脱机电子邮件,但是自动名称生成器将点放在文件名中,有时它们出现在文件的开头。 查找程序似乎无法获取具有该名称的文件-那些文件被隐藏了……有人经历过这种行为吗?有什么建议如何使用
我正在尝试进行 LDAP 身份验证,我目前遇到此类错误: ServiceNotFoundException: The service "security.firewall.map.context.ma
有没有办法验证和检查集合数组是否为空。我已经尝试过: /** * @Assert\NotBlank() * @Assert\Length( min = 1) */ protected $work
使用Smyfony2和Doctrin2,可以使用以下示例创建数据固定装置:http://symfony.com/doc/current/bundles/DoctrineFixturesBundle/i
我看到在大多数Symfony 2示例中,例如,不存在记录时,Symfony 2会引发异常。我认为这种方法对最终用户不友好。为什么有人更喜欢引发异常而不在Flashbag上添加一些错误消息? 最佳答案
我对项目中的以下服务有疑问: app.security.guardAuthenticatorLoginPassword: class: AppBundle\Security\LoginPa
symfony缓存和登录Docker容器存在问题。 Web服务器从www-data用户和组执行,当我使用docker上安装的php从docker容器中清除symfony缓存时,它从root执行。 因此
我想了解 symfony 中的服务 我已阅读http://symfony.com/doc/2.3/book/service_container.html#creating-configuring-se
因为我对 Symfony 和 Doctrine 还很陌生,所以我有一个可能很愚蠢的问题;-) 有人可以用简单的词语向我解释集合(尤其是实体中的ArrayCollections)吗?它是什么以及何时以及
我收到了这个表格: {{ form_start(form) }} {{ form_end(form) }} 我想检查用户是否登录,我这样做了: {% if is_g
我的网站已准备好部署,我正在尝试将其设置为在线。 一些信息: 主持人是 OVH。 它不允许 SSH,我必须使用 FTP 发送文件。也没有命令行。 我现在希望能够在子目录中设置网站:/www/test(
过去几个月以来,我一直在尝试与symfony合作。昨晚我自动删除了不需要的存储库。之后,我无法使用symfony命令创建新的symfony项目。当我在终端中运行Symfony new Security
In the environnement variable, then in system variable, I edited the path and added在环境变量中,然后在系统变量
我们有一个 Symfony 1.4 应用程序,想升级到 Symfony 4。是否有可能或者我们必须重新编程该应用程序? 我们询问了我们附近的一家软件公司,他们告诉我们必须重新编写应用程序。 最佳答案
我是一名优秀的程序员,十分优秀!