찾다
웹 프론트엔드JS 튜토리얼C/C++로 node.js 네이티브 모듈을 작성하는 방법에 대한 자세한 설명

이 기사에서는 주로 C/C++를 사용하여 node.js 네이티브 모듈을 작성하는 방법에 대해 소개합니다. 구현 단계는 필요한 모든 사람을 위한 특정 참조 및 학습 가치를 단계별로 소개합니다. 아래 에디터와 함께 살펴보겠습니다.

머리말

저는 항상 C/C++를 사용하여 nodejs 네이티브 모듈을 작성하는 방법을 알고 싶었습니다. 인터넷에서 찾은 대부분의 블로그에는 환경을 설정하고 마무리하는 방법이 나와 있습니다. 안녕하세요 세계. 더 이상 참고자료도 없습니다. 그래서 제가 직접 정리해서 여기에 공유했습니다.

환경 준비에 관해서는 온라인에 많이 있어서 자세한 내용은 다루지 않겠습니다.

주로 두 곳을 참고하세요:

  • nodejs 공식 문서

  • v8 문서

첫 번째는 몇 가지 좋은 참고 사례를 소개하는 nodejs의 공식 문서입니다.

두 번째는 C++용 v8 엔진에 대한 문서입니다. C++ 모듈을 작성할 때 주로 읽어야 할 문서입니다.

좋아요, 몇 가지 예부터 시작해 C++를 사용하여 nodejs 모듈을 작성하는 방법을 단계별로 이해해 보겠습니다.

Hello World

Hello World를 먼저 작성해 봅시다. 결국 프로그래머가 가장 먼저 아는 프로그램은 Hello World입니다.


#include <node.h>
void hello(const v8::FunctionCallbackInfo<v8::Value> &args) {
 v8::Isolate *isolate = args.GetIsolate();
 auto message = v8::String::NewFromUtf8(isolate, "Hello World!");
 args.GetReturnValue().Set(message);
}
void Initialize(v8::Local<v8::Object> exports) {
 NODE_SET_METHOD(exports, "hello", hello);
}
NODE_MODULE(module_name, Initialize)

좋아요, 이것은 가장 간단한 HelloWorld입니다. 파일 이름을 addon.cc로 지정합니다. node-gyp를 사용하여 컴파일한 다음 직접 require를 사용하여 js 파일에 모듈을 삽입합니다. .


const myAddon = require(&#39;./build/Release/addon&#39;) ;
console.log(myAddon.hello());

예기치 않은 일이 발생하지 않으면 Hello World!가 터미널에 인쇄됩니다.

코드를 간단히 살펴보겠습니다. 첫 번째 줄 #include <node.h></node.h>는 C++에서 node.h 헤더 파일을 소개하는 코드입니다. 헤더 파일은 인터페이스로 이해될 수 있으며, 인터페이스 메소드만 정의하고 이를 구현하지는 않습니다. C++ 링커는 두 파일을 함께 연결하는 역할을 합니다. #include <node.h></node.h>是C++中引入node.h头文件的代码。头文件可理解为接口,我们在里面只定义了接口方法,并未实现,然后通过其他文件实现,C++链接器负责将这两个链接在一起。

然后定义了一个方法hello() ,没有返回值。方法参数通过const v8::FunctionCallbackInfo<:value> &args</:value>传递,注意,这里我们加了v8::前缀注解,也可以直接在文件开始使用using v8;这样就可以不用每次都使用这个注解了。

v8::Isolate *isolate = args.GetIsolate();这里,我们在函数中访问了javascript的作用域。

auto message = v8::String::NewFromUtf8(isolate, "Hello World!");我们创建了一个字符串类型的变量,赋值Hello World!并将其绑定到作用域。

我们通过args.GetReturnValue()获取了我们函数的返回值

Initialize()方法用于初始化模块方法,将方法和要导出的模块的方法名进行绑定。

最后NODE_MODULE导出这个模块。

上面这个例子很简单,如果是js代码的话:


&#39;use strict&#39;;
let hello = function hello() {
 let message = "Hello World!";
 return message;
};
module.exports = {
 hello:hello
};

好了,第一个HelloWorld就结束了。网上很多介绍nodejs C++模块的博客文章,到这里就结束了。看完之后,一脸懵逼,啥啊这是?我想再写个传参数,并对参数做简单操作的方法该怎么写?

sum(a,b)

好吧。那我们就再写一个sum(a,b)函数,传递两个数字类型参数a,b,并求两个参数的和返回。

js中代码简单到下:


let sum = function(a,b){
 if(typeof a == &#39;number&#39; && typeof b == &#39;number&#39;){
  return a + b;
 }else{
  throw new Error(&#39;参数类型错误&#39;);
 }
}

那么,C++该如何编写:


void sum(const FunctionCallbackInfo<Value> &args) {
 Isolate *isolate = args.GetIsolate();
 if(!args[0]->IsNumber()){
  isolate->ThrowException(v8::Exception::TypeError(
   v8::String::NewFromUtf8(isolate, "args[0] must be a number")));
  return ;
 }
 if(!args[1]->IsNumber()){
  isolate->ThrowException(v8::Exception::TypeError(
   v8::String::NewFromUtf8(isolate, "args[1] must be a number")));
  return ;
 }
 args.GetReturnValue().Set(args[0]->NumberValue() + args[1]->NumberValue());
}

首先判断两个参数是否是Number类型,如果不是,直接抛出异常。如果是,则将返回值设置为两个参数的和。

这里我们并没有在参数列表中,直接使用a,b作为参数,而是直接使用 args 对象。 这和js是类似的,第一个参数是 args[0] ,第二个参数是 args[1]

调用IsNumber()来判断是否是数字类型。如果不是,抛出一个TypeError类型错误异常。
如果类型没问题,使用args[0]->NumberValue()获取参数的数字值,然后相加,赋值给返回值。

可能你会问,args[0] 这是个啥?它的IsNumber()

그런 다음 반환 값이 없는 hello() 메서드가 정의됩니다. 메소드 매개변수는 const v8::FunctionCallbackInfo<:value> &args</:value>를 통해 전달됩니다. 여기서는 v8:: 접두사 주석을 직접 사용할 수도 있습니다. /code> 이렇게 하면 매번 이 주석을 사용할 필요가 없습니다.

v8::Isolate *isolate = args.GetIsolate();여기서 함수의 javascript 범위에 액세스합니다.

auto message = v8::String::NewFromUtf8(isolate, "Hello World!"); 문자열 유형 변수를 생성하고 Hello World! 값을 할당하고 Scope에 바인딩했습니다.

args.GetReturnValue()를 통해 함수의 반환을 얻습니다. 가치.

🎜Initialize() 메서드는 모듈 메서드를 초기화하고 내보낼 모듈의 메서드 이름에 메서드를 바인딩하는 데 사용됩니다. 🎜🎜마지막으로 NODE_MODULE은 이 모듈을 내보냅니다. 🎜🎜위의 예는 js 코드라면 매우 간단합니다. 🎜🎜🎜🎜
let sumOfArray = function(array){
 if(!Array.isArray(array)){
  throw new Error(&#39;参数错误,必须为Array类型&#39;);
 }
 let sum = 0;
 for(let item of array){
  sum += item;
 }
 return sum;
}
🎜자, 첫 번째 HelloWorld가 끝났습니다. nodejs C++ 모듈을 소개하는 블로그 기사가 인터넷에 많이 있지만 여기에서 끝납니다. 읽고 나니 헷갈리네요, 이게 뭐죠? 매개변수를 전달하고 매개변수에 대한 간단한 작업을 수행하는 다른 메서드를 작성하고 싶습니다. 어떻게 작성해야 합니까? 🎜🎜🎜🎜sum(a,b)🎜🎜🎜🎜🎜알겠습니다. 그런 다음 또 다른 sum(a,b) 함수를 작성하고 두 개의 숫자 유형 매개변수 a, b를 전달하고 두 매개변수의 합계를 찾아 반환합니다. 🎜🎜js의 코드는 다음과 같이 간단합니다. 🎜🎜🎜🎜
void sumOfArray(const FunctionCallbackInfo<Value> &args){
 Isolate *isolate = args.GetIsolate();
 if(!args[0]->IsArray()){
  isolate->ThrowException(v8::Exception::TypeError(
   v8::String::NewFromUtf8(isolate, "args[0] must be an Array")));
  return ;
 }
 Local<Object> received_v8_obj = args[0]->ToObject();
 Local<Array> keys = received_v8_obj->GetOwnPropertyNames();
 int length = keys->Length();
 double sum = 0;
 for(int i=0;i<length;i++){
  sum += received_v8_obj->Get(keys->Get(i))->NumberValue();
 }
 args.GetReturnValue().Set(sum);
}
🎜그래서 C++로 작성하는 방법: 🎜🎜🎜🎜
void createObj(const FunctionCallbackInfo<Value> &args){
  Isolate *isolate = args.GetIsolate();
  Local<Object> obj = Object::New(isolate);
  obj->Set(String::NewFromUtf8(isolate,"name"),args[0]->ToString());
  obj->Set(String::NewFromUtf8(isolate,"age"),args[1]->ToNumber());
  args.GetReturnValue().Set(obj);
}
🎜먼저 두 매개변수가 숫자 유형인지 확인합니다. 그렇지 않으면 직접 예외가 발생합니다. 그렇다면 반환 값은 두 인수의 합계로 설정됩니다. 🎜🎜여기에서는 매개변수 목록에서 a와 b를 매개변수로 직접 사용하지 않고 args 객체를 직접 사용합니다. 이는 js와 유사합니다. 첫 번째 매개변수는 args[0] 이고 두 번째 매개변수는 args[1] 입니다. 🎜🎜숫자 유형인지 확인하려면 IsNumber()를 호출하세요. 그렇지 않은 경우 TypeError 예외를 발생시킵니다. 🎜유형이 정상이면 args[0]->NumberValue()를 사용하여 매개변수의 숫자 값을 가져온 다음 이를 추가하고 반환 값에 할당합니다. 🎜🎜args[0] 이게 뭐죠?라고 물어보실 수도 있습니다. IsNumber() 메서드는 어디에서 왔나요? 문서는 어디서 찾을 수 있나요? 🎜🎜이것은 실제로 v8 엔진의 내부 유형으로, 기본적으로 js의 내장 개체에 일대일로 대응합니다. v8 유형 문서를 확인할 수 있습니다. 🎜🎜🎜🎜🎜위 그림은 js의 타입 시스템과 매우 비슷하죠? 🎜🎜🎜JS의 배열, 날짜, 함수, 문자열 등은 모두 Object에서 상속되고 v8 엔진 내부에서는 Object 및 Primitive가 모두 Value 유형에서 상속됩니다. 🎜

这里的IsNumber()方法就是Value类型的方法。那么除了这个方法,还有什么方法呢?

上面这张图,我只是截了一小部分,全部的可以直接去查阅文档。看,这里有各种方法,判断是否是数字类型的IsNumber(),判断是否是日期类型的IsDate() ,判断是否是数组的IsArray()方法等等。

v8的接口实现的也很完善了,即使并不精通C++的开发者也可以照猫画虎的实现个简单的模块。

args[0]->NumberValue()返回的是一个double的值,是的,这里是实打实的C++里的double类型,可以直接进行加减运算的。类似的还有BooleanValue()方法等等,都是获取不同类型的值使用的方法。

第二个例子中,我们简单实现了一个sum()方法,传递两个参数,求和。但是这里涉及到的只是整型的值,那如果有其他类型的值怎么办呢?比如数组。

sumOfArray(array)

下面将方法升级一下,传递一个数组,然后求数组中所有值的和。js的话:


let sumOfArray = function(array){
 if(!Array.isArray(array)){
  throw new Error(&#39;参数错误,必须为Array类型&#39;);
 }
 let sum = 0;
 for(let item of array){
  sum += item;
 }
 return sum;
}

逻辑很简单,就是将传过来的数组进行遍历一遍,然后将所有项累加即可。C++也是如此:


void sumOfArray(const FunctionCallbackInfo<Value> &args){
 Isolate *isolate = args.GetIsolate();
 if(!args[0]->IsArray()){
  isolate->ThrowException(v8::Exception::TypeError(
   v8::String::NewFromUtf8(isolate, "args[0] must be an Array")));
  return ;
 }
 Local<Object> received_v8_obj = args[0]->ToObject();
 Local<Array> keys = received_v8_obj->GetOwnPropertyNames();
 int length = keys->Length();
 double sum = 0;
 for(int i=0;i<length;i++){
  sum += received_v8_obj->Get(keys->Get(i))->NumberValue();
 }
 args.GetReturnValue().Set(sum);
}

先判断是否是数组,没什么问题。

然后我们定义了一个Object类型的received_v8_obj属性,将其赋值为args[0]->ToObject() 。这里调用ToObject()方法将其转换为一个对象。

然后调用这个对象的GetOwnPropertyNames()方法获取所有的键,然后根据键获取对象的值,进行累加。

为什么不直接将其转换为数组,然后进行遍历呢?

我们都知道,js中的数组并不是真正的数组,其实质还是对象。其内部都是键值对存储的。因此这里也是一样,Value类型并不提供直接转换为数组的ToArray()方法,而是将其转换为Object对象,通过对象的形式进行操作。

那么对象有哪些操作呢,看文档。

但是你会发现,v8确实有个Array类,继承自Object类。那么Array有什么方法呢?

看文档就知道了,少的可怜:

所以,对数组的操作都将转换为对象操作。

createObj()

说到对象了,那么我们就来写一个创建对象的方法。传递两个参数,一个name,一个age,创建一个对象,表示一个人,名叫啥,多大年纪。


void createObj(const FunctionCallbackInfo<Value> &args){
  Isolate *isolate = args.GetIsolate();
  Local<Object> obj = Object::New(isolate);
  obj->Set(String::NewFromUtf8(isolate,"name"),args[0]->ToString());
  obj->Set(String::NewFromUtf8(isolate,"age"),args[1]->ToNumber());
  args.GetReturnValue().Set(obj);
}

这个方法,参照文档,基本没啥可说的。

通过Object::New(isolate)创建一个对象,然后设置两个属性name,age,将参数依次赋值给这两个属性,然后返回这个对象即可。

如果用js写:


let createObj = function(name,age){
  let obj = {};
  obj.name = name;
  obj.age = age;
  return obj;
};

callback

上面说的,都没提到js中一个重要的东西,回调函数。如果参数中传一个回调函数,那么我们该如何执行呢?

来一个简单的例子。


let cb = function(a,b,fn){
  if(typeof a !== &#39;number&#39; || typeof b !== &#39;number&#39;){
    throw new Error(&#39;参数类型错误,只能是Number类型&#39;);
  }
  if(typeof fn !== &#39;function&#39;){
    throw new Error(&#39;参数fn类型错误,只能是Function类型&#39;);
  }
  fn(a,b);
};

这个例子很简单,我们传两个数字类型参数a,b和一个回调函数fn,然后将a,b作为fn的参数调用fn回调函数。这里我们对a,b的操作转交给回调函数。回调函数里我们可以求和,也可以求积,随你。

这个例子中,暂时还没涉及到的是如何调用回调函数。

先上代码:


void cb(const FunctionCallbackInfo<Value> &args){
  Isolate *isolate = args.GetIsolate();
  if(!args[0]->IsNumber()){
    isolate->ThrowException(v8::Exception::TypeError(
      v8::String::NewFromUtf8(isolate, "args[0] must be a Number")));
  }
  if(!args[1]->IsNumber()){
    isolate->ThrowException(v8::Exception::TypeError(
      v8::String::NewFromUtf8(isolate, "args[1] must be a Number")));
  }
  if(!args[2]->IsFunction()){
    isolate->ThrowException(v8::Exception::TypeError(
      v8::String::NewFromUtf8(isolate, "args[2] must be a Function")));
  }
  Local<Function> jsfn = Local<Function>::Cast(args[2]);
  Local<Value> argv[2] = { Number::New(isolate,args[0]->NumberValue()),Number::New(isolate,args[1]->NumberValue())};
  Local<Value> c = jsfn->Call(Null(isolate),2,argv);
  args.GetReturnValue().Set(c);
}

上面三个判断参数类型,略过。

我们定义一个Function类型属性jsfn,将args[2]强制转换为Function并赋值给jsfn。

然后定义一个具有两个值的参数argv,这两个值就是args[0] , args[1]的数字值。

然后通过jsfn->Call(Null(isolate),2,argv)调用回调函数。

argv是一个数组,其个数我们在定义时指定,2个。

Call()方法为函数类型的值进行调用的方法。

Local< Value > | Call (Handle< Value > recv, int argc, Handle< Value > argv[])

查阅文档,可以看出,Call()方法传3个参数,第一个参数是执行上下文,用于绑定代码执行时的this,第二个参数为参数个数,第三个为参数列表,数组形式。

上面几个例子,只是冰山一角,连一角都算不上。只为了解一下nodejs使用C/C++编写原生模块,如果要编写一个可用的,高性能的C模块,那么,要求程序员一定要精通C/C++,并且对js底层也很精通,包括v8和libuv等等。

위 내용은 C/C++로 node.js 네이티브 모듈을 작성하는 방법에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
파이썬과 자바 스크립트의 미래 : 트렌드와 예측파이썬과 자바 스크립트의 미래 : 트렌드와 예측Apr 27, 2025 am 12:21 AM

Python 및 JavaScript의 미래 추세에는 다음이 포함됩니다. 1. Python은 과학 컴퓨팅 분야에서의 위치를 ​​통합하고 AI, 2. JavaScript는 웹 기술의 개발을 촉진하고, 3. 교차 플랫폼 개발이 핫한 주제가되고 4. 성능 최적화가 중점을 둘 것입니다. 둘 다 해당 분야에서 응용 프로그램 시나리오를 계속 확장하고 성능이 더 많은 혁신을 일으킬 것입니다.

Python vs. JavaScript : 개발 환경 및 도구Python vs. JavaScript : 개발 환경 및 도구Apr 26, 2025 am 12:09 AM

개발 환경에서 Python과 JavaScript의 선택이 모두 중요합니다. 1) Python의 개발 환경에는 Pycharm, Jupyternotebook 및 Anaconda가 포함되어 있으며 데이터 과학 및 빠른 프로토 타이핑에 적합합니다. 2) JavaScript의 개발 환경에는 Node.js, VScode 및 Webpack이 포함되어 있으며 프론트 엔드 및 백엔드 개발에 적합합니다. 프로젝트 요구에 따라 올바른 도구를 선택하면 개발 효율성과 프로젝트 성공률이 향상 될 수 있습니다.

JavaScript가 C로 작성 되었습니까? 증거를 검토합니다JavaScript가 C로 작성 되었습니까? 증거를 검토합니다Apr 25, 2025 am 12:15 AM

예, JavaScript의 엔진 코어는 C로 작성되었습니다. 1) C 언어는 효율적인 성능과 기본 제어를 제공하며, 이는 JavaScript 엔진 개발에 적합합니다. 2) V8 엔진을 예를 들어, 핵심은 C로 작성되며 C의 효율성 및 객체 지향적 특성을 결합하여 C로 작성됩니다.

JavaScript의 역할 : 웹 대화식 및 역동적 인 웹JavaScript의 역할 : 웹 대화식 및 역동적 인 웹Apr 24, 2025 am 12:12 AM

JavaScript는 웹 페이지의 상호 작용과 역학을 향상시키기 때문에 현대 웹 사이트의 핵심입니다. 1) 페이지를 새로 고치지 않고 콘텐츠를 변경할 수 있습니다. 2) Domapi를 통해 웹 페이지 조작, 3) 애니메이션 및 드래그 앤 드롭과 같은 복잡한 대화식 효과를 지원합니다. 4) 성능 및 모범 사례를 최적화하여 사용자 경험을 향상시킵니다.

C 및 JavaScript : 연결이 설명되었습니다C 및 JavaScript : 연결이 설명되었습니다Apr 23, 2025 am 12:07 AM

C 및 JavaScript는 WebAssembly를 통한 상호 운용성을 달성합니다. 1) C 코드는 WebAssembly 모듈로 컴파일되어 컴퓨팅 전력을 향상시키기 위해 JavaScript 환경에 도입됩니다. 2) 게임 개발에서 C는 물리 엔진 및 그래픽 렌더링을 처리하며 JavaScript는 게임 로직 및 사용자 인터페이스를 담당합니다.

웹 사이트에서 앱으로 : 다양한 JavaScript 애플리케이션웹 사이트에서 앱으로 : 다양한 JavaScript 애플리케이션Apr 22, 2025 am 12:02 AM

JavaScript는 웹 사이트, 모바일 응용 프로그램, 데스크탑 응용 프로그램 및 서버 측 프로그래밍에서 널리 사용됩니다. 1) 웹 사이트 개발에서 JavaScript는 HTML 및 CSS와 함께 DOM을 운영하여 동적 효과를 달성하고 jQuery 및 React와 같은 프레임 워크를 지원합니다. 2) 반응 및 이온 성을 통해 JavaScript는 크로스 플랫폼 모바일 애플리케이션을 개발하는 데 사용됩니다. 3) 전자 프레임 워크를 사용하면 JavaScript가 데스크탑 애플리케이션을 구축 할 수 있습니다. 4) node.js는 JavaScript가 서버 측에서 실행되도록하고 동시 요청이 높은 높은 요청을 지원합니다.

Python vs. JavaScript : 사용 사례 및 응용 프로그램 비교Python vs. JavaScript : 사용 사례 및 응용 프로그램 비교Apr 21, 2025 am 12:01 AM

Python은 데이터 과학 및 자동화에 더 적합한 반면 JavaScript는 프론트 엔드 및 풀 스택 개발에 더 적합합니다. 1. Python은 데이터 처리 및 모델링을 위해 Numpy 및 Pandas와 같은 라이브러리를 사용하여 데이터 과학 및 기계 학습에서 잘 수행됩니다. 2. 파이썬은 간결하고 자동화 및 스크립팅이 효율적입니다. 3. JavaScript는 프론트 엔드 개발에 없어서는 안될 것이며 동적 웹 페이지 및 단일 페이지 응용 프로그램을 구축하는 데 사용됩니다. 4. JavaScript는 Node.js를 통해 백엔드 개발에 역할을하며 전체 스택 개발을 지원합니다.

JavaScript 통역사 및 컴파일러에서 C/C의 역할JavaScript 통역사 및 컴파일러에서 C/C의 역할Apr 20, 2025 am 12:01 AM

C와 C는 주로 통역사와 JIT 컴파일러를 구현하는 데 사용되는 JavaScript 엔진에서 중요한 역할을합니다. 1) C는 JavaScript 소스 코드를 구문 분석하고 추상 구문 트리를 생성하는 데 사용됩니다. 2) C는 바이트 코드 생성 및 실행을 담당합니다. 3) C는 JIT 컴파일러를 구현하고 런타임에 핫스팟 코드를 최적화하고 컴파일하며 JavaScript의 실행 효율을 크게 향상시킵니다.

See all articles

핫 AI 도구

Undresser.AI Undress

Undresser.AI Undress

사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover

AI Clothes Remover

사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool

Undress AI Tool

무료로 이미지를 벗다

Clothoff.io

Clothoff.io

AI 옷 제거제

Video Face Swap

Video Face Swap

완전히 무료인 AI 얼굴 교환 도구를 사용하여 모든 비디오의 얼굴을 쉽게 바꾸세요!

뜨거운 도구

Dreamweaver Mac版

Dreamweaver Mac版

시각적 웹 개발 도구

SublimeText3 Mac 버전

SublimeText3 Mac 버전

신 수준의 코드 편집 소프트웨어(SublimeText3)

Eclipse용 SAP NetWeaver 서버 어댑터

Eclipse용 SAP NetWeaver 서버 어댑터

Eclipse를 SAP NetWeaver 애플리케이션 서버와 통합합니다.

MinGW - Windows용 미니멀리스트 GNU

MinGW - Windows용 미니멀리스트 GNU

이 프로젝트는 osdn.net/projects/mingw로 마이그레이션되는 중입니다. 계속해서 그곳에서 우리를 팔로우할 수 있습니다. MinGW: GCC(GNU Compiler Collection)의 기본 Windows 포트로, 기본 Windows 애플리케이션을 구축하기 위한 무료 배포 가능 가져오기 라이브러리 및 헤더 파일로 C99 기능을 지원하는 MSVC 런타임에 대한 확장이 포함되어 있습니다. 모든 MinGW 소프트웨어는 64비트 Windows 플랫폼에서 실행될 수 있습니다.

PhpStorm 맥 버전

PhpStorm 맥 버전

최신(2018.2.1) 전문 PHP 통합 개발 도구