キーポイント
- Story Daoは、OpenzePpelinの所有可能な契約を使用して、所有者のみが管理機能を実行できるようにし、DAPP運用のセキュリティと制御を強化します。
- Story DAO契約には、調整可能な料金と期間パラメーターがあり、不正な変更を防ぐためのセキュリティ対策が装備されており、所有者のみが重要な設定を変更できるようにします。 ストーリーのホワイトリスト管理DAOは、送信者の貢献に基づいて自動および条件付きアクセスを可能にする有料機能を通じて実装されています。
- SolitidityやJavaScriptテストを含む 統合されたテスト戦略は、ストーリーDAOの機能とセキュリティを検証し、展開前に堅牢な操作を確保するために重要です。
- ストーリーDAOの展開プロセスは、特定の移行スクリプトと構成によりトリュフによって簡素化され、開発環境から生産環境へのスムーズな移行が容易になります。
- このチュートリアルシリーズの第3部では、イーサリアムを使用してDAPPの構築について説明しています。ここでは、トークンをEthereum Test Network Rinkebyに展開します。このセクションでは、Story Daoコードの作成を開始します。
入門記事にリストされている基準を使用して、導きます。
契約の概要新しい契約を作成しましょう。Storydao.sol。そのフレームワークは次のとおりです。
SafeMathをインポートして安全な計算を再度行いますが、今回はZeppelinの所有可能な契約も使用します。これにより、誰かがストーリーを「所有」し、特定の管理者のみの機能を実行できます。簡単に言えば、StoryDaoが所有可能になるだけで十分です。
また、この契約では唯一の所有者修飾子を使用しています。関数修飾子は、基本的に機能の拡張機能とプラグインです。唯一の所有者モディファイアは次のようになります:pragma solidity ^0.4.24; import "../node_modules/openzeppelin-solidity/contracts/math/SafeMath.sol"; import "../node_modules/openzeppelin-solidity/contracts/ownership/Ownable.sol"; contract StoryDao is Ownable { using SafeMath for uint256; mapping(address => bool) whitelist; uint256 public whitelistedNumber = 0; mapping(address => bool) blacklist; event Whitelisted(address addr, bool status); event Blacklisted(address addr, bool status); uint256 public daofee = 100; // 百分之几,即 100 为 1% uint256 public whitelistfee = 10000000000000000; // 以 Wei 为单位,这是 0.01 以太币 event SubmissionCommissionChanged(uint256 newFee); event WhitelistFeeChanged(uint256 newFee); uint256 public durationDays = 21; // 故事章节持续时间(天) uint256 public durationSubmissions = 1000; // 故事章节持续时间(条目) function changedaofee(uint256 _fee) onlyOwner external { require(_fee <= 1000); // 限制最大费用为 10% daofee = _fee; emit SubmissionCommissionChanged(_fee); } function changewhitelistfee(uint256 _fee) onlyOwner external { require(_fee > 0); // 确保费用大于 0 whitelistfee = _fee; emit WhitelistFeeChanged(_fee); } function changeDurationDays(uint256 _days) onlyOwner external { require(_days >= 1); durationDays = _days; } function changeDurationSubmissions(uint256 _subs) onlyOwner external { require(_subs > 99); durationSubmissions = _subs; } }
関数に所有者のみが追加されると、関数の本体が_に貼り付けられ、前の部分が最初に実行されます。したがって、この修飾子を使用することにより、関数はメッセージ送信者が契約の所有者であるかどうかを自動的にチェックし、それが真である場合は通常どおり継続します。そうでない場合、それはクラッシュします。
ストーリーDAOの料金やその他のパラメーターを変更する関数に唯一の所有者修飾子を使用することにより、管理者のみがこれらの変更を行うことができるようにします。
modifier onlyOwner() { require(msg.sender == owner); _; }
テスト
初期関数をテストしましょう。
存在しない場合は、フォルダーテストを作成します。次に、ファイルTestStoryDao.solとtestStorydao.jsを作成します。トリュフには例外をテストするためのネイティブ方法がないため、ヘルパー/expectThrow.jsも以下を使用して作成されます。
注:堅牢性テストは通常、低レベルの契約ベースの機能、つまりスマートコントラクトの内部構造をテストするために使用されます。 JSテストは、契約が外部から正しく相互作用できるかどうかをテストするためによく使用されます。これは、エンドユーザーが行うことです。export default async promise => { try { await promise; } catch (error) { const invalidOpcode = error.message.search('invalid opcode') >= 0; const outOfGas = error.message.search('out of gas') >= 0; const revert = error.message.search('revert') >= 0; assert( invalidOpcode || outOfGas || revert, 'Expected throw, got \'' + error + '\' instead', ); return; } assert.fail('Expected throw not received'); };testStorydao.solで、次のコンテンツを置きます:
pragma solidity ^0.4.24; import "../node_modules/openzeppelin-solidity/contracts/math/SafeMath.sol"; import "../node_modules/openzeppelin-solidity/contracts/ownership/Ownable.sol"; contract StoryDao is Ownable { using SafeMath for uint256; mapping(address => bool) whitelist; uint256 public whitelistedNumber = 0; mapping(address => bool) blacklist; event Whitelisted(address addr, bool status); event Blacklisted(address addr, bool status); uint256 public daofee = 100; // 百分之几,即 100 为 1% uint256 public whitelistfee = 10000000000000000; // 以 Wei 为单位,这是 0.01 以太币 event SubmissionCommissionChanged(uint256 newFee); event WhitelistFeeChanged(uint256 newFee); uint256 public durationDays = 21; // 故事章节持续时间(天) uint256 public durationSubmissions = 1000; // 故事章节持续时间(条目) function changedaofee(uint256 _fee) onlyOwner external { require(_fee <= 1000); // 限制最大费用为 10% daofee = _fee; emit SubmissionCommissionChanged(_fee); } function changewhitelistfee(uint256 _fee) onlyOwner external { require(_fee > 0); // 确保费用大于 0 whitelistfee = _fee; emit WhitelistFeeChanged(_fee); } function changeDurationDays(uint256 _days) onlyOwner external { require(_days >= 1); durationDays = _days; } function changeDurationSubmissions(uint256 _subs) onlyOwner external { require(_subs > 99); durationSubmissions = _subs; } }
これにより、StoryDao契約が正しい料金と期間の数値で適切に展開されていることが確認されます。最初の行では、展開されたアドレスリストから読み取ることで展開されることが保証され、最後のセクションでは>アサーション - 宣言が真か偽かを確認します。この例では、展開された契約の初期値と数値を比較します。それが「真」であるときはいつでも、Assert.equalsセクションは「真の」と述べたイベントを発行します。これは、トリュフがテスト中に聞いていることです。
testStorydao.jsでは、次のコンテンツを置きますテストが正常に実行されるためには、ストーリーダオを展開したいことをトリュフに伝える必要があります。それでは、以前に書いた移行とほぼ同じコンテンツを使用して、3_Deploy_StoryDao.jsを移行中に作成しましょう。
modifier onlyOwner() { require(msg.sender == owner); _; }この時点で、これまでに必要な依存関係と将来必要なものを含むプロジェクトフォルダーのルートにあるパッケージファイルを更新(またはそうでない場合は作成)する必要があります。 >
および.babelrcファイルを含むファイル:
export default async promise => { try { await promise; } catch (error) { const invalidOpcode = error.message.search('invalid opcode') >= 0; const outOfGas = error.message.search('out of gas') >= 0; const revert = error.message.search('revert') >= 0; assert( invalidOpcode || outOfGas || revert, 'Expected throw, got \'' + error + '\' instead', ); return; } assert.fail('Expected throw not received'); };
また、トリュフの構成にバベルが要求される必要があるため、テストをコンパイルするときに使用する必要があることがわかります。
pragma solidity ^0.4.24; import "truffle/Assert.sol"; import "truffle/DeployedAddresses.sol"; import "../contracts/StoryDao.sol"; contract TestStoryDao { function testDeploymentIsFine() public { StoryDao sd = StoryDao(DeployedAddresses.StoryDao()); uint256 daofee = 100; // 百分之几,即 100 为 1% uint256 whitelistfee = 10000000000000000; // 以 Wei 为单位,这是 0.01 以太币 uint256 durationDays = 21; // 故事章节持续时间(天) uint256 durationSubmissions = 1000; // 故事章节持续时间(条目) Assert.equal(sd.daofee(), daofee, "初始 DAO 费用应为 100"); Assert.equal(sd.whitelistfee(), whitelistfee, "初始白名单费用应为 0.01 以太币"); Assert.equal(sd.durationDays(), durationDays, "初始天数持续时间应设置为 3 周"); Assert.equal(sd.durationSubmissions(), durationSubmissions, "初始提交持续时间应设置为 1000 个条目"); } }
注:BabelはNodejsのアドオンであり、現在の世代のNodejsで次世代JavaScriptを使用できるようにするため、インポートやその他のコンテンツを書くことができます。これを理解していない場合は、それを無視して逐語的に貼り付けてください。インストール後、この問題に二度と対処する必要がない場合があります。
import expectThrow from './helpers/expectThrow'; const StoryDao = artifacts.require("StoryDao"); contract('StoryDao Test', async (accounts) => { it("should make sure environment is OK by checking that the first 3 accounts have over 20 eth", async () =>{ assert.equal(web3.eth.getBalance(accounts[0]).toNumber() > 2e+19, true, "Account 0 has more than 20 eth"); assert.equal(web3.eth.getBalance(accounts[1]).toNumber() > 2e+19, true, "Account 1 has more than 20 eth"); assert.equal(web3.eth.getBalance(accounts[2]).toNumber() > 2e+19, true, "Account 2 has more than 20 eth"); }); it("should make the deployer the owner", async () => { let instance = await StoryDao.deployed(); assert.equal(await instance.owner(), accounts[0]); }); it("should let owner change fee and duration", async () => { let instance = await StoryDao.deployed(); let newDaoFee = 50; let newWhitelistFee = 1e+10; // 1 ether let newDayDuration = 42; let newSubsDuration = 1500; instance.changedaofee(newDaoFee, {from: accounts[0]}); instance.changewhitelistfee(newWhitelistFee, {from: accounts[0]}); instance.changeDurationDays(newDayDuration, {from: accounts[0]}); instance.changeDurationSubmissions(newSubsDuration, {from: accounts[0]}); assert.equal(await instance.daofee(), newDaoFee); assert.equal(await instance.whitelistfee(), newWhitelistFee); assert.equal(await instance.durationDays(), newDayDuration); assert.equal(await instance.durationSubmissions(), newSubsDuration); }); it("should forbid non-owners from changing fee and duration", async () => { let instance = await StoryDao.deployed(); let newDaoFee = 50; let newWhitelistFee = 1e+10; // 1 ether let newDayDuration = 42; let newSubsDuration = 1500; await expectThrow(instance.changedaofee(newDaoFee, {from: accounts[1]})); await expectThrow(instance.changewhitelistfee(newWhitelistFee, {from: accounts[1]})); await expectThrow(instance.changeDurationDays(newDayDuration, {from: accounts[1]})); await expectThrow(instance.changeDurationSubmissions(newSubsDuration, {from: accounts[1]})); }); it("should make sure the owner can only change fees and duration to valid values", async () =>{ let instance = await StoryDao.deployed(); let invalidDaoFee = 20000; let invalidDayDuration = 0; let invalidSubsDuration = 98; await expectThrow(instance.changedaofee(invalidDaoFee, {from: accounts[0]})); await expectThrow(instance.changeDurationDays(invalidDayDuration, {from: accounts[0]})); await expectThrow(instance.changeDurationSubmissions(invalidSubsDuration, {from: accounts[0]})); }) });
さあ、最終的にトリュフテストを実行します。出力はこれに似ている必要があります:
var Migrations = artifacts.require("./Migrations.sol"); var StoryDao = artifacts.require("./StoryDao.sol"); module.exports = function(deployer, network, accounts) { if (network == "development") { deployer.deploy(StoryDao, {from: accounts[0]}); } else { deployer.deploy(StoryDao); } };テストの詳細については、このチュートリアルを参照してください。これは、スマートコントラクトのテストをカバーするために特別に準備しました。
このコースの次のセクションでは、逐語的に入力するとチュートリアルが長すぎるため、テストをスキップしますが、プロジェクトの最終ソースコードを参照してすべてのテストを確認してください。完了したばかりのプロセスには、テスト用の環境が設定されているため、さらにセットアップせずにテストを作成できます。
ホワイトリスト
ユーザーがストーリーの構築に参加できるようにするホワイトリストメカニズムを構築しましょう。以下の関数フレームワークをStoryDao.SOLに追加します:
無名の関数関数()はフォールバック関数と呼ばれます。これは、この契約にファンドが送信されたが特定の指示はありません(つまり、他の関数は特異的に呼ばれない)と呼ばれます。これにより、人々はエーテルをDAOのみに送り、ホワイトリストに登録されているかどうかに基づいて、すぐにホワイトリストまたはトークンを購入することでStoryDaoに参加できます。 ホワイトリストの関数はホワイトリストに使用され、直接呼び出すことができますが、送信者がまだホワイトリストに登録されていない場合、フォールバック関数がエーテルを受け取った後に自動的に呼び出すことを確認します。 WhitelistAddress関数は、他の契約からも呼び出されるべきであるため公開されますが、ファンドは外部住所からこのアドレスに送信されるため、フォールバック関数は外部です。この契約を呼び出す契約は、必要な関数を直接簡単に呼び出すことができます。
注:これは安全な計算のためのセーフマス関数であるため、subの代わりに - subを使用します。
結論
このチュートリアルでは、DAOの最初の部分を構築しましたが、まだやるべきことがたくさんあります。ご期待ください:次のセクションでは、ストーリーにコンテンツを追加することを扱います! イーサリアムダップとホワイトリストの構築に関するFAQ
スマート契約は自己執行契約であり、その契約条件はコードに直接記述されます。彼らは、ブロックチェーン上のビジネスロジックの実行を自動化するため、Dappsで重要な役割を果たします。展開されたら、変更したり改ざんしたりすることができないため、透明性、セキュリティ、および不変性を確保します。 テストは、その機能とセキュリティを確保するためのDAPP開発の重要な部分です。トリュフやガナッシュなどのツールを使用してテストできます。トリュフは、イーサリアムに開発環境、テストフレームワーク、アセットパイプラインを提供しますが、Ganacheを使用すると、テスト用のプライベートイーサリアムブロックチェーンを作成できます。 daoは分散型の自律組織を表します。これは、透明性があり、組織メンバーによって管理され、中央政府の影響を受けないコンピュータープログラムとしてエンコードされたルールで表される組織タイプです。 DAOの金融取引とルールはブロックチェーンに保持されているため、DAPPになります。 DAPPのセキュリティにはさまざまなプラクティスが含まれることを確認します。これには、安全なスマートコントラクトの作成、徹底的なテスト、セキュリティ監査の実行、ソフトウェアと依存関係を最新の状態に保つことが含まれます。また、安全なコーディングのためにベストプラクティスに従い、ブロックチェーンスペースの最新のセキュリティの脆弱性と脅威に遅れないことも重要です。 Metamaskは、ブラウザから直接Ethereumブロックチェーンと対話できるブラウザ拡張機能です。また、イーサリアムとERC-20トークンを管理するためのイーサリアムウォレットとしても機能します。ユーザーが完全なイーサリアムノードを実行せずにDAPPと対話できるようにユーザーフレンドリーなインターフェイスをユーザーに提供するため、DAPP開発において重要です。 DAPPを開発およびテストしたら、Ethereum MainNetまたはTestNetに展開できます。これには、スマートコントラクトをコンパイルし、ブロックチェーンに展開し、DAPPをそれらの契約に接続することが含まれます。トリュフやインフラなどのツールを使用して、このプロセスを完了できます。 DAPP開発はいくつかの課題に直面しています。これには、Ethereum Networkのスケーラビリティの問題の処理、DAPPのセキュリティの確保、取引の揮発性ガス価格の管理、ユーザーフレンドリーなインターフェイスの提供が含まれます。また、急速に進化するブロックチェーンテクノロジーと規制に注意する必要があります。 pragma solidity ^0.4.24;
import "../node_modules/openzeppelin-solidity/contracts/math/SafeMath.sol";
import "../node_modules/openzeppelin-solidity/contracts/ownership/Ownable.sol";
contract StoryDao is Ownable {
using SafeMath for uint256;
mapping(address => bool) whitelist;
uint256 public whitelistedNumber = 0;
mapping(address => bool) blacklist;
event Whitelisted(address addr, bool status);
event Blacklisted(address addr, bool status);
uint256 public daofee = 100; // 百分之几,即 100 为 1%
uint256 public whitelistfee = 10000000000000000; // 以 Wei 为单位,这是 0.01 以太币
event SubmissionCommissionChanged(uint256 newFee);
event WhitelistFeeChanged(uint256 newFee);
uint256 public durationDays = 21; // 故事章节持续时间(天)
uint256 public durationSubmissions = 1000; // 故事章节持续时间(条目)
function changedaofee(uint256 _fee) onlyOwner external {
require(_fee <= 1000); // 限制最大费用为 10%
daofee = _fee;
emit SubmissionCommissionChanged(_fee);
}
function changewhitelistfee(uint256 _fee) onlyOwner external {
require(_fee > 0); // 确保费用大于 0
whitelistfee = _fee;
emit WhitelistFeeChanged(_fee);
}
function changeDurationDays(uint256 _days) onlyOwner external {
require(_days >= 1);
durationDays = _days;
}
function changeDurationSubmissions(uint256 _subs) onlyOwner external {
require(_subs > 99);
durationSubmissions = _subs;
}
}
送信者がまだホワイトリストに登録されていないかどうかを確認し、ホワイトリスタドレス機能への呼び出しを委任します。まだ持っていないので、BuyTokens関数にコメントしたことに注意してください。 modifier onlyOwner() {
require(msg.sender == owner);
_;
}
この関数は、(トランザクションから)メッセージから抽出するのではなく、引数としてアドレスとして受け入れることに注意してください。これには、誰かがDAOに参加する余裕がない場合、他の人をホワイトリストに登録できるという利点があります。 イーサリアムDAPPを構築するための前提条件は何ですか?
DAPPSにおけるスマートコントラクトの役割は何ですか?
私のdappをテストする方法は?
DAOとは何ですか?また、DAPPとどう関係していますか?
私のdappのセキュリティを確保する方法は?
メタマスクとは何ですか?また、なぜDAPP開発において重要なのですか?
私のdappを展開する方法は?
DAPP開発の課題は何ですか?
展開後にDAPPを更新するにはどうすればよいですか?
ブロックチェーン上のスマートコントラクトが不変であるため、展開後のDAPPSの更新は困難な場合があります。ただし、データとロジックを異なる契約に分離したり、委任されたコールを使用して契約をアップグレードすることにより、アップグレード可能な契約を設計できます。 DAPPの設計段階でのアップグレードと変更の計画は非常に重要です。
以上がビルディングイーサリアムダップ:ストーリーダオのホワイトリストとテストの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

AIの分野での中国の女性の技術力の台頭:Deepseekの女性の技術分野への貢献とのコラボレーションの背後にある物語は、ますます重要になっています。中国の科学技術省からのデータは、女性科学技術労働者の数が巨大であり、AIアルゴリズムの開発においてユニークな社会的価値感度を示していることを示しています。この記事では、携帯電話の名誉に焦点を当て、その背後にある女性チームの強さを探り、Deepseek Bigモデルに最初に接続し、技術の進歩を促進し、技術開発の価値座標系を再構築する方法を示します。 2024年2月8日、Honorは、Deepseek-R1 FullbloodバージョンのBig Modelを正式に立ち上げ、Android Campで最初のメーカーになり、Deepseekに接続し、ユーザーから熱狂的な反応を喚起しました。この成功の背後にある女性チームメンバーは、製品の決定、技術的なブレークスルー、ユーザーを行っています

DeepseekはZhihuに関する技術記事をリリースし、Deepseek-V3/R1推論システムを詳細に導入し、初めて主要な財務データを開示し、業界の注目を集めました。この記事は、システムの毎日のコストの利益率が545%に高いことを示しており、グローバルなAIビッグモデルの利益の新たな高値を設定しています。 Deepseekの低コスト戦略は、市場競争において有利になります。モデルトレーニングのコストは同様の製品の1%から5%であり、V3モデルトレーニングのコストはわずか5576百万米ドルであり、競合他社のコストよりもはるかに低くなっています。一方、R1のAPI価格設定は、Openaio3-Miniの1/7〜1/2です。これらのデータは、DeepSeekテクノロジールートの商業的実現可能性を証明し、AIモデルの効率的な収益性も確立します。

ウェブサイトの構築は最初のステップに過ぎません:SEOとバックリンクの重要性 ウェブサイトを構築することは、それを貴重なマーケティング資産に変換するための最初のステップにすぎません。検索エンジンでのWebサイトの可視性を向上させ、潜在的な顧客を引き付けるために、SEO最適化を行う必要があります。バックリンクは、ウェブサイトのランキングを改善するための鍵であり、Googleや他の検索エンジンにWebサイトの権限と信頼性を示しています。 すべてのバックリンクが有益であるわけではありません:有害なリンクを特定して回避する すべてのバックリンクが有益であるわけではありません。有害なリンクはあなたのランキングに害を及ぼす可能性があります。優れた無料のバックリンクチェックツールは、ウェブサイトへのリンクのソースを監視し、有害なリンクを思い出させます。さらに、競合他社のリンク戦略を分析し、それらから学ぶこともできます。 無料のバックリンクチェックツール:SEOインテリジェンスオフィサー

Mideaは、Deepseek Big Model -Midea Fresh and Clean Air Machine T6を装備した最初のエアコンをリリースします。このエアコンには、環境に応じて温度、湿度、風速などのパラメーターをインテリジェントに調整できる、高度な空気インテリジェントな駆動システムが装備されています。さらに重要なことは、DeepSeek Big Modelを統合し、400,000を超えるAI Voiceコマンドをサポートすることです。 Mideaの動きは、業界での激しい議論を引き起こし、特に白物と大規模なモデルを組み合わせることの重要性を懸念しています。従来のエアコンの単純な温度設定とは異なり、Midea Fresh and Clean Air Machine T6は、より複雑で曖昧な指示を理解し、家庭環境に従って湿度をインテリジェントに調整し、ユーザーエクスペリエンスを大幅に改善します。

DeepSeek-R1はBaidu LibraryとNetDiskを支援します。深い思考と行動の完璧な統合は、わずか1か月で多くのプラットフォームに迅速に統合されました。大胆な戦略的レイアウトにより、BaiduはDeepSeekをサードパーティモデルのパートナーとして統合し、それをそのエコシステムに統合します。これは、「ビッグモデル検索」の生態学的戦略の大きな進歩を示しています。 Baidu SearchとWenxin Intelligent Intelligent Platformは、DeepSeekおよびWenxin Bigモデルの深い検索関数に最初に接続し、ユーザーに無料のAI検索エクスペリエンスを提供します。同時に、「Baiduに行くときにあなたが知っている」という古典的なスローガンとBaiduアプリの新しいバージョンは、WenxinのBig ModelとDeepseekの機能も統合し、「AI検索」と「ワイドネットワーク情報の改良」を起動します。

このGOベースのネットワーク脆弱性スキャナーは、潜在的なセキュリティの弱点を効率的に識別します。 Goの同時機能機能を速度で活用し、サービスの検出と脆弱性のマッチングが含まれます。その能力と倫理を探りましょう

コード生成のAIプロンプトエンジニアリング:開発者ガイド コード開発の風景は、大きな変化を遂げています。 大規模な言語モデル(LLMS)と迅速なエンジニアリングのマスタリングは、今後数年間で開発者にとって非常に重要です。 th


ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

AI Hentai Generator
AIヘンタイを無料で生成します。

人気の記事

ホットツール

SublimeText3 英語版
推奨: Win バージョン、コードプロンプトをサポート!

MinGW - Minimalist GNU for Windows
このプロジェクトは osdn.net/projects/mingw に移行中です。引き続きそこでフォローしていただけます。 MinGW: GNU Compiler Collection (GCC) のネイティブ Windows ポートであり、ネイティブ Windows アプリケーションを構築するための自由に配布可能なインポート ライブラリとヘッダー ファイルであり、C99 機能をサポートする MSVC ランタイムの拡張機能が含まれています。すべての MinGW ソフトウェアは 64 ビット Windows プラットフォームで実行できます。

メモ帳++7.3.1
使いやすく無料のコードエディター

PhpStorm Mac バージョン
最新(2018.2.1)のプロフェッショナル向けPHP統合開発ツール

ZendStudio 13.5.1 Mac
強力な PHP 統合開発環境

ホットトピック



