Compiling PHP's Eval() to .NET

栏目: IT技术 · 发布时间: 4年前

内容简介:We havealready mentioned the support of PHP’sFirst and foremost, whenever

We havealready mentioned the support of PHP’s eval() function a long time ago, but there have been some interesting updates on how the code being passed to eval() is processed!

eval is a well-known function of dynamic languages allowing you to execute code at runtime. The dynamic language is usually interpreted and the runtime contains the parser and interpreter already, so implementing such a feature is no rocket science. Still…

It’s very difficult to debug and analyze eval – that’s where the compilation and .NET come into place.

First and foremost, whenever eval is observed in the code, the compiler reports a warning diagnostic. This language construct allows for an execution of arbitrary code; therefore, PeachPie throws a warning as a precaution.

Features we’d like

So what makes eval() so evil? :smiling_imp:

  • It can potentially be dangerous
  • You don’t see the code
  • You can’t debug the code
  • It won’t get analyzed by your development tools
  • It generally indicates a design flaw, since the developer couldn’t write what they intended the code to do in the first place

Working with eval’ed code is simply inconvenient. Your development environment has to do a lot of work to allow uninterrupted debugging and exception handling.

Invocation of eval() at Run Time

PeachPie takes advantage of .NET and Roslyn whenever possible. This feature is a nice demonstration of the power of .NET, which isn’t needed as much in other typical .NET languages.

The runtime instantiates the whole compiler (PhpCompilation derived from Compilation Class ) and caches its instance for eventual subsequent calls to another eval() . Here goes the first trick:

PortablePDB and Embedded Source

Setting the compilation’s options to use the new PortablePDB allows us to take advantage of the Embedded Source feature. This means that we only need to remember the original code that’s being evaluated when it’s needed during debugging. And it’s all right there in the debug information in the standard .NET way! As a result, any CoreCLR debugger will be able to “step into” the code that only exists in memory.

Compiling PHP's Eval() to .NET
dotPeek : browsing the PortabPDB metadata. index.php.eval2.php is a made up file path.

In order to make this feature work, it is important to choose a virtual file path for our virtual embedded source code. The path must not contain any special characters (like $ , ~ , < , > or ` ) and must not exist on the file system. The Embedded Source itself must have a checksum using the SHA256 hashing algorithm.

if (options.EmitDebugInformation) { // == Debugger.IsAttached
  compilation = compilation
    .WithOptions(compilation.Options
        .WithOptimizationLevel(OptimizationLevel.Debug)
        .WithDebugPlusMode(true));

  emitOptions = emitOptions
    .WithDebugInformationFormat(DebugInformationFormat.PortablePdb);
  embeddedTexts = new[] {
    EmbeddedText.FromSource(tree.FilePath, tree.GetText())
  };
}

The snippet above sets up the compiler. EmitDebugInformation is set according to Debugger.IsAttached – we simply want to emit the debug information of eval() only if we are just debugging the program. Otherwise it’s just an unnecessary performance overhead.

Emit and Load!

result = compilation.Emit(peStream, pdbStream, emitOptions, embeddedTexts);

Roslyn’s compilation has a neat API method Emit , which according to our options performs the compilation – in-memory. It writes the content of the assembly and debug information into System.IO.Stream s – peStream and pdbStream . All the diagnostics are populated into the result object for further inspection; we might for example forbid the execution of code containing any warning or just a specific warning.

assembly = System.Runtime.Loader.AssemblyLoadContext
  .Default
  .LoadFromStream(peStream, pdbStream)

We keep things in memory only and load our resulting assembly from the stream. Assuming you have a debugger attached, it will load the PDB and process it. Using reflection, we find the script entry point from the loaded assembly (in case of PHP scripts it’s a static method <eval>`xxx.<Main>(Context, PhpArray, object, RuntimeTypeHandle) . It takes all the parameters it needs to properly execute within PHP code – runtimeContext, array with local variables, reference to $this object and the current type context.

The method is then invoked! The rest is handled by the CoreCLR debugger. If you press F11 in your favorite IDE, the debugger will jump over the compilation stuff and steps right inside the invoked method. The debugger will start looking for the source code, which is annotated to be in our virtual file path. It finds it within the loaded debug information and displays it to you!

Visual Studio, for instance, will create a temporary file, copies the content of Embedded Source document into it and opens it in the editor. Also, it remembers the mapping between the original virtual file path and allows you to put breakpoints in there.

Visual Studio Code will open a virtual document for this purpose allowing you to do the same.

Make things nicer

A small touch is to make the virtual Embedded Source text colorized when opened by the debugger in your IDE. Fortunately, this is not that hard. First, the virtual file path has to have the right file extension, in our case .php . (The other option would be to implement a custom language service for the IDE as an extension).

Compiling PHP's Eval() to .NET
Debugging a virtual embedded source document

Next, since the code passed to eval() in PHP omits the opening code tags ( <?php ), the editor syntax highlighter would treat the code as text or HTML. So before compiling the code, we prefix it with the commented opening tag: #<?php . The hash is treated as a line comment, but the editor views it as HTML text, and <?php as the opening tag. The rest of the code is nicely colorized. (the other editor features then work as well – tooltips, structure guidelines, outlining, etc.)

To Summarize

I don’t think Embedded Source and Roslyn’s compilation were designed for dynamic languages in the first place, but it seems like a perfect use case for those features. This greatly improves the debugging experience of compiled dynamic languages – out of the box.

Just a side note – this works for a few other dynamic documents in PHP that cause other IDEs headaches – create_function() , eval() and debugging inside the PHAR archives, which are embedded into PDB as well, at compile-time already.


以上所述就是小编给大家介绍的《Compiling PHP's Eval() to .NET》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

京东平台运营攻略(全彩)

京东平台运营攻略(全彩)

京东商学院 / 电子工业出版社 / 2015-5 / 69.00元

2014 年年末,京东POP 开放平台的入驻商家已超过6 万,京东平台被广泛关注和认可的同时,在电商江湖中仍颇具神秘色彩。面对碎片化的信息,京东的店铺经营者及希望入驻京东的准商家们,对于在京东如何利用丰富的各类平台资源,搭建并运营京东店铺,一直很难找到全面而系统的资料。 《京东平台运营攻略(全彩)》由京东官方出品,动员了京东内部涉及第三方店铺业务线的众多部门,由多位业务精英参与撰写,保证了内......一起来看看 《京东平台运营攻略(全彩)》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具