カスタムビューのXCode表示
@IBDesignable class MyView: UIView { @IBInspectable var cornerRadius : CGFloat = 0.0; // Only override draw() if you perform custom drawing. // An empty implementation adversely affects performance during animation. override func draw(_ rect: CGRect) { // Drawing code self.layer.cornerRadius = cornerRadius if cornerRadius > 0 { self.clipsToBounds = true; } } }
木構造的なもの
LISPのようなものを作ろうとしたら単に文字列の結合を木構造でやるということになっていた。でもクラスについてだいぶ勉強になった。
// // RDLisp.hpp // RDLisp // // Created by KatagiriSo on 2017/09/26. // Copyright © 2017年 RodhosSoft. All rights reserved. // #ifndef RDLisp_hpp #define RDLisp_hpp #include <iostream> #include <stdio.h> namespace RDLisp { template<class T> using Share = std::shared_ptr<T>; class Printable { public: virtual std::string description() = 0; }; class S : public Printable{ public: }; class Value; class Pair : public S { public: Share<S> car; Share<S> cdr; Pair(Share<S> car, Share<S> cdr) { this->car = car; this->cdr = cdr; } std::string description(); bool isFunc(); bool isValue(); }; class Nil : public S { public: Nil(); std::string description(); }; class Symbol; class Value; class Atom : public S { public: std::string value; Atom(std::string value) { this->value = value; }; std::string description(); }; class Value : public Atom { public: Value(std::string value) : Atom(value) { }; }; /// symbol is function class Symbol : public Atom { public: Symbol(std::string value) : Atom(value) { }; }; Share<S> eval(Share<S> s); Share<S> apply(Share<Symbol> sy, Share<S> target); } #endif /* RDLisp_hpp */
// // RDLisp.cpp // RDLisp // // Created by KatagiriSo on 2017/09/26. // Copyright © 2017年 RodhosSoft. All rights reserved. // #include "RDLisp.hpp" #include <vector> #include <string> namespace RDLisp { template <class T> Share<T> scast(Share <S> s) { return std::dynamic_pointer_cast<T>(s); } Share<Value> add(Share<S> target, int n) { std::cout <<std::string(n, ' ') << "+"<< "\n"; if (auto v = scast<Value>(target)) { std::cout << std::string(n, ' ')<< v->description() << "\n"; return v; } if (auto p = scast<Pair>(target)) { if (auto s = scast<Symbol>(p->car)) { auto cdr_after = apply(s, p->cdr); if (auto v = scast<Value>(cdr_after)) { return v; } else { return nullptr; } } if (auto f = scast<Value>(p->car)) { auto l = add(p->cdr, n+1); if (f&&l) { auto v = std::make_shared<Value>(f->value + l->value); std::cout <<std::string(n, ' ')<< "["<< v->description() << "]"<< "\n"; return v; } else { return nullptr; } } if (auto l = scast<Value>(p->cdr)) { auto f = add(p->car, n+1); if (f&&l) { auto v = std::make_shared<Value>(f->value + l->value); std::cout <<std::string(n, ' ')<< "["<< v->description() << "]"<< "\n"; return v; } else { return nullptr; } } auto f = add(p->car, n+1); auto l = add(p->cdr, n+1); if (f&&l) { auto v = std::make_shared<Value>(f->value + l->value); std::cout <<std::string(n, ' ')<< "["<< v->description() << "]"<< "\n"; return v; } else { return nullptr; } } std::cout <<std::string(n, ' ')<< "nullptr"<< "\n"; return nullptr; } Share<S> eval(Share<S> exp) { auto pair = scast<Pair>(exp); if (pair) { if (pair->isFunc()) { auto sy = std::dynamic_pointer_cast<Symbol>(pair->car); // auto crd_after = eval(pair->cdr); // std::cout << "crd_after" + crd_after->description() + "\n"; return apply(sy, pair->cdr); // return apply(sy, crd_after); } } return exp; } Share<S> apply(Share<Symbol> sy, Share<S> target) { if (sy->value == "+") { if (auto v = scast<Value>(target)) { return target; } if (auto p = scast<Pair>(target)) { auto exp = add(target, 0); if (exp) { return exp; } } } return std::make_shared<Pair>(sy,target); } bool Pair::isFunc() { auto sy = std::dynamic_pointer_cast<Symbol>(this->car); if (sy) { return true; } return false; } std::string Pair::description() { return "(" + this->car->description() + " " + this->cdr->description() + ")"; } std::string Atom::description() { return this->value; } std::string Nil::description() { return ""; } }
実行は次のようにする。面倒。
// // main.cpp // RDLisp // // Created by KatagiriSo on 2017/09/26. // Copyright © 2017年 RodhosSoft. All rights reserved. // #include <iostream> #include "RDLisp.hpp" int main(int argc, const char * argv[]) { // insert code here... std::cout << "Hello, World!\n"; using namespace RDLisp; using namespace std; auto a1 = make_shared<Value>("a1"); auto a2 = make_shared<Value>("a2"); auto pair_a = make_shared<Pair>(a1,a2); auto b = make_shared<Value>("b"); auto c = make_shared<Value>("c"); auto d = make_shared<Value>("d"); auto add = make_shared<Symbol>("+"); auto add2 = make_shared<Symbol>("+"); auto pair_ab = make_shared<Pair>(pair_a,b); auto pair_cd = make_shared<Pair>(c,d); auto pair = make_shared<Pair>(add, pair_ab); auto pair2 = make_shared<Pair>(add, pair_cd); auto pair3 = make_shared<Pair>(pair, pair2); auto pair4 = make_shared<Pair>(add2, pair3); std::cout << pair4->description() << "\n"; auto res = eval(pair4); std::cout << res->description() << "\n"; return 0; }
結果は
Hello, World! (+ ((+ ((a1 a2) b)) (+ (c d)))) + + + + + a2 [a1a2] [a1a2b] + + + d [cd] [a1a2bcd] a1a2bcd Program ended with exit code: 0
練習 文字列等
文字列等の練習にコマンドと引数の対をとるコードを書いてみた。引数には対自体を入れることができるとする。
結果はこんな感じになる。
>+ * 100 (+ (* ( 100)))
#ifndef RDCalc_hpp #define RDCalc_hpp #include <stdio.h> #include "RDConfig.hpp" class Command { public: Command() = default; ~Command() = default; std::string name; std::string description(); }; class Value; class Term { public: Term() = default; ~Term() = default; std::shared_ptr<Command> command; std::shared_ptr<Term> value; std::string name; std::string description(); }; class Value : public Term { public: Value(); ~Value() = default; }; class RDCalc { public: RDCalc(); ~RDCalc(); void start(); std::string calc(std::string); std::shared_ptr<Term> state; private: std::shared_ptr<Term> getTerm(std::vector<std::string>); }; #endif /* RDCalc_hpp */
// // RDCalc.cpp // RDCalcSystem // // Created by KatagiriSo on 2017/09/26. // Copyright © 2017年 RodhosSoft. All rights reserved. // #include <sstream> #include <iostream> #include "RDCalc.hpp" #include<string> #include<vector> RDCalc::RDCalc() { } RDCalc::~RDCalc() { std::cout << "RDCalc dec.\n"; } void RDCalc::start() { std::string input; while(true) { std::cout << ">"; std::string str; std::vector<std::string> v; std::getline(std::cin, str); auto output = RDCalc::calc(str); std::cout << output << std::endl; } } std::vector<std::string> splitString(std::string input) { std::stringstream ss; ss << input; std::vector<std::string> v; std::string com; while(ss >> com) { v.push_back(com); } return v; } std::string RDCalc::calc(std::string input) { std::vector<std::string> v = splitString(input); this->state = getTerm(v); if (this->state == nullptr) { return "?\n"; } return this->state->description(); } std::shared_ptr<Term> RDCalc::getTerm(std::vector<std::string> v) { auto term = std::make_shared<Term>(); auto command = std::make_shared<Command>(); // if (m=="+") { // auto m = v.front(); v.erase(v.begin()); if (v.empty()) { term->command = command; term->command->name = ""; auto value = std::make_shared<Value>(); value->name = m; term->value = value; } else { command->name = m; term->command = command; term->value = getTerm(v); } return term; } std::string Term::description() { if (this->value == nullptr) { return this->name; } return "(" + this->command->description() + " " + this->value->description() + ")"; } std::string Command::description() { return this->name; } Value::Value() { command = std::make_shared<Command>(); command->name = ""; }
たまに使うショートカット
⌘ { でタブ移動
⌘デリート でその文頭からカーソルまで削除
⌘→ 行末へ移動
⌥エスケープ 補完リスト表示
⌘T 新しいタブ作成
コマンドライン
コンパイルする場合はswiftcを使う。
$ swiftc -help OVERVIEW: Swift compiler USAGE: swiftc [options] <inputs> MODES: -dump-ast Parse and type-check input file(s) and dump AST(s) -dump-parse Parse input file(s) and dump AST(s) -dump-scope-maps <expanded-or-list-of-line:column> Parse and type-check input file(s) and dump the scope map(s) -dump-type-refinement-contexts Type-check input file(s) and dump type refinement contexts(s) -emit-assembly Emit assembly file(s) (-S) -emit-bc Emit LLVM BC file(s) -emit-executable Emit a linked executable -emit-ir Emit LLVM IR file(s) -emit-library Emit a linked library -emit-object Emit object file(s) (-c) -emit-sibgen Emit serialized AST + raw SIL file(s) -emit-sib Emit serialized AST + canonical SIL file(s) -emit-silgen Emit raw SIL file(s) -emit-sil Emit canonical SIL file(s) -parse Parse input file(s) -print-ast Parse and type-check input file(s) and pretty print AST(s) -typecheck Parse and type-check input file(s) OPTIONS: -application-extension Restrict code to those available for App Extensions -assert-config <value> Specify the assert_configuration replacement. Possible values are Debug, Release, Unchecked, DisableReplacement. -continue-building-after-errors Continue building, even after errors are encountered -driver-time-compilation Prints the total time it took to execute all compilation tasks -D <value> Marks a conditional compilation flag as true -embed-bitcode-marker Embed placeholder LLVM IR data as a marker -embed-bitcode Embed LLVM IR bitcode as data -emit-dependencies Emit basic Make-compatible dependencies files -emit-module-path <path> Emit an importable module to <path> -emit-module Emit an importable module -emit-objc-header-path <path> Emit an Objective-C header file to <path> -emit-objc-header Emit an Objective-C header file -fixit-all Apply all fixits from diagnostics without any filtering -fixit-code Get compiler fixits as code edits -framework <value> Specifies a framework which should be linked against -F <value> Add directory to framework search path -gdwarf-types Emit full DWARF type info. -gline-tables-only Emit minimal debug info for backtraces only -gnone Don't emit debug info -g Emit debug info. This is the preferred setting for debugging with LLDB. -help Display available options -import-underlying-module Implicitly imports the Objective-C half of a module -index-store-path <path> Store indexing data to <path> -I <value> Add directory to the import search path -j <n> Number of commands to execute in parallel -L <value> Add directory to library link search path -l<value> Specifies a library which should be linked against -module-cache-path <value> Specifies the Clang module cache path -module-link-name <value> Library to link against when using this module -module-name <value> Name of the module to build -nostdimport Don't search the standard library import path for modules -num-threads <n> Enable multi-threading and specify number of threads -Onone Compile without any optimization -Ounchecked Compile with optimizations and remove runtime safety checks -output-file-map <path> A file which specifies the location of outputs -O Compile with optimizations -o <file> Write output to <file> -parse-as-library Parse the input file(s) as libraries, not scripts -parse-sil Parse the input file as SIL code, not Swift source -parseable-output Emit textual output in a parseable format -profile-coverage-mapping Generate coverage data for use with profiled execution counts -profile-generate Generate instrumented code to collect execution counts -sanitize-coverage=<type> Specify the type of coverage instrumentation for Sanitizers and additional options separated by commas -sanitize=<check> Turn on runtime checks for erroneous behavior. -save-temps Save intermediate compilation results -sdk <sdk> Compile against <sdk> -serialize-diagnostics Serialize diagnostics in a binary format -static-executable Statically link the executable -static-stdlib Statically link the Swift standard library -suppress-warnings Suppress all warnings -swift-version <vers> Interpret input according to a specific Swift language version number -target-cpu <value> Generate code for a particular CPU variant -target <value> Generate code for the given target -tools-directory <directory> Look for external executables (ld, clang, binutils) in <directory> -use-ld=<value> Specifies the linker to be used -version Print version information and exit -v Show commands to run and use verbose output -warnings-as-errors Treat warnings as errors -whole-module-optimization Optimize input files together instead of individually -Xcc <arg> Pass <arg> to the C/C++/Objective-C compiler -Xlinker <value> Specifies an option which should be passed to the linker
そしてswiftというコマンドもある。
$ swift -h OVERVIEW: Swift compiler USAGE: swift [options] <inputs> OPTIONS: -assert-config <value> Specify the assert_configuration replacement. Possible values are Debug, Release, Unchecked, DisableReplacement. -continue-building-after-errors Continue building, even after errors are encountered -D <value> Marks a conditional compilation flag as true -framework <value> Specifies a framework which should be linked against -F <value> Add directory to framework search path -gdwarf-types Emit full DWARF type info. -gline-tables-only Emit minimal debug info for backtraces only -gnone Don't emit debug info -g Emit debug info. This is the preferred setting for debugging with LLDB. -help Display available options -index-store-path <path> Store indexing data to <path> -I <value> Add directory to the import search path -j <n> Number of commands to execute in parallel -L <value> Add directory to library link search path -l<value> Specifies a library which should be linked against -module-cache-path <value> Specifies the Clang module cache path -module-link-name <value> Library to link against when using this module -module-name <value> Name of the module to build -nostdimport Don't search the standard library import path for modules -num-threads <n> Enable multi-threading and specify number of threads -Onone Compile without any optimization -Ounchecked Compile with optimizations and remove runtime safety checks -O Compile with optimizations -sdk <sdk> Compile against <sdk> -static-executable Statically link the executable -static-stdlib Statically link the Swift standard library -suppress-warnings Suppress all warnings -swift-version <vers> Interpret input according to a specific Swift language version number -target-cpu <value> Generate code for a particular CPU variant -target <value> Generate code for the given target -use-ld=<value> Specifies the linker to be used -version Print version information and exit -v Show commands to run and use verbose output -warnings-as-errors Treat warnings as errors -Xcc <arg> Pass <arg> to the C/C++/Objective-C compiler -Xlinker <value> Specifies an option which should be passed to the linker
Swiftは対話モードで実行できる。
$ swift Welcome to Apple Swift version 3.1 (swiftlang-802.0.53 clang-802.0.42). Type :help for assistance. 1> 3+2 $R0: Int = 5 2> class A { 3. var hoge:Int? = nil 4. func hello() { 5. print("hello \(hoge)") 6. } 7. } 8> let a = A() a: A = { hoge = nil } 9> a.hoge = 100 10> a.hello() hello Optional(100) 11>
画像
画像処理のコードを書いた。CFDataがNSDataのブリッジなことに気づかなかった..。
文字列ストリーム風
以下でStream(Array(src.characters))としてやれば文字列stream風になる。
class Stream<T> { let m_value:[T] var count = 0 init(_ v:[T]) { m_value = v } func get()->T? { if count < m_value.count { let ret = m_value[count] count = count + 1 print("\(ret)") return ret } else { return nil } } func unget() { count = count - 1 print("unget") if count < 0 { count = count + 1 } } }
GCDの例
SwiftでGCDのgroup等の使い方のサンプルを書いてみた。
// // ViewController.swift // GCDSample // // Created by KatagiriSo on 2017/09/19. // Copyright © 2017年 RodhosSoft. All rights reserved. // import UIKit class ViewController: UIViewController , UITableViewDelegate , UITableViewDataSource{ struct Menu { let name:String } let menu:[Menu] = [Menu(name: NSStringFromSelector(#selector(AsyncBarrier))), Menu(name: NSStringFromSelector(#selector(GroupWait))), Menu(name: NSStringFromSelector(#selector(GroupNotify))), Menu(name: NSStringFromSelector(#selector(GroupEnterLeave))), Menu(name: NSStringFromSelector(#selector(SemaphoExample))) ] func makeEqualConstraint(item:Any, toItem:Any, attribute:NSLayoutAttribute) -> NSLayoutConstraint{ return NSLayoutConstraint(item: item, attribute: attribute, relatedBy: .equal, toItem: toItem, attribute: attribute, multiplier: 1.0, constant: 0) } override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. let tableView:UITableView = UITableView(frame: self.view.bounds) tableView.delegate = self tableView.dataSource = self tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell") self.view.addSubview(tableView) let l = makeEqualConstraint(item: tableView, toItem: self.view, attribute: .left) let r = makeEqualConstraint(item: tableView, toItem: self.view, attribute: .right) let t = makeEqualConstraint(item: tableView, toItem: self.view, attribute: .top) let b = makeEqualConstraint(item: tableView, toItem: self.view, attribute: .bottom) l.isActive = true r.isActive = true t.isActive = true b.isActive = true } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } func numberOfSections(in tableView: UITableView) -> Int { return 1 } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return menu.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) cell.textLabel?.text = menu[indexPath.row].name return cell } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { let selector = NSSelectorFromString(menu[indexPath.row].name) self.perform(selector) } func AsyncBarrier() { print("\(#function)") let queue = DispatchQueue(label: "asyncBarrierTest") [1,2,3,4,5,6,7,8,9,10].forEach { n in queue.async { print("done \(n)") } } queue.async(flags:.barrier) { print("all done") } print("\(#function) end") } func GroupWait() { print("\(#function)") let queue = DispatchQueue(label: "groupWaitTest") let group = DispatchGroup() for n in 1...100 { queue.async(group: group) { print("done \(n)") } } print("group.wait") let _ = group.wait(timeout: DispatchTime.distantFuture) print("\(#function) end") } func GroupNotify() { print("\(#function)") let queue = DispatchQueue(label: "GroupNotify") let group = DispatchGroup() for n in 1...100 { queue.async(group: group) { print("done \(n)") } } group.notify(queue: queue) { print("done") } print("\(#function) end") } func GroupEnterLeave() { print("\(#function)") let queue = DispatchQueue(label: "GroupNotify") let group = DispatchGroup() for n in 1...100 { group.enter() queue.async { print("done \(n)") group.leave() } } group.notify(queue: queue) { print("done") } print("\(#function) end") } func SemaphoExample() { print("\(#function)") let semapho = DispatchSemaphore(value: 0) // semapho.wait() DispatchQueue.global().async { for n in 1...100 { print("count \(n)") } semapho.signal() } print("wait") semapho.wait() print("\(#function) end") } }
View
rederによってViewがレンダリングされる。その部分をみてみる。
public function render($view = null, $layout = null) { // すでにレンダリングされているなら終わり if ($this->hasRendered) { return null; } // レイアウトがメソッドの引数に指定されているならそれをレイアウトとして使う $defaultLayout = null; if ($layout !== null) { $defaultLayout = $this->layout; $this->layout = $layout; } $viewFileName = $view !== false ? $this->_getViewFileName($view) : null; if ($viewFileName) { $this->_currentType = static::TYPE_TEMPLATE; /// レンダリングイベント通知 $this->dispatchEvent('View.beforeRender', [$viewFileName]); /// _render関数でレイアウトし、ブロックのcontentとしてsetする。 $this->Blocks->set('content', $this->_render($viewFileName)); /// レンダリング終了通知 $this->dispatchEvent('View.afterRender', [$viewFileName]); } /// レウアウトがあるならrenderLayoutでレンダリングし、ブロックのcontentとしてセットする。 if ($this->layout && $this->autoLayout) { $this->Blocks->set('content', $this->renderLayout('', $this->layout)); } if ($layout !== null) { $this->layout = $defaultLayout; } $this->hasRendered = true; /// ブロックのcontentを取得し返却する。 return $this->Blocks->get('content'); }
_render関数、ここが本体になる。テンプレートの継承などもここで解決する。
protected function _render($viewFile, $data = []) { // 引数で$dataが指定されていなければviewVarsを$dataとして使う。 if (empty($data)) { $data = $this->viewVars; } $this->_current = $viewFile; $initialBlocks = count($this->Blocks->unclosed()); // イベント通知 $this->dispatchEvent('View.beforeRenderFile', [$viewFile]); // テンプレートを_evaluateする。ここでファイル内のコードが実行される? $content = $this->_evaluate($viewFile, $data); // イベント通知 $afterEvent = $this->dispatchEvent('View.afterRenderFile', [$viewFile, $content]); if ($afterEvent->getResult() !== null) { $content = $afterEvent->getResult(); } // ここで継承しているテンプレートがあればアサインし if (isset($this->_parents[$viewFile])) { $this->_stack[] = $this->fetch('content'); $this->assign('content', $content); // 親のレンダリング $content = $this->_render($this->_parents[$viewFile]); $this->assign('content', array_pop($this->_stack)); } $remainingBlocks = count($this->Blocks->unclosed()); if ($initialBlocks !== $remainingBlocks) { throw new LogicException(sprintf( 'The "%s" block was left open. Blocks are not allowed to cross files.', $this->Blocks->active() )); } return $content; }
$this->Blocksのところ、ビューブロックをおさえる必要がある。
ViewBuilder
ビューを作る。
build()のところで作っている。
public function build($vars = [], ServerRequest $request = null, Response $response = null, EventManager $events = null) { $className = $this->_className; if ($className === null) { $className = App::className('App', 'View', 'View') ?: 'Cake\View\View'; } if ($className === 'View') { $className = App::className($className, 'View'); } else { $className = App::className($className, 'View', 'View'); } if (!$className) { throw new MissingViewException(['class' => $this->_className]); } $data = [ 'name' => $this->_name, 'templatePath' => $this->_templatePath, 'template' => $this->_template, 'plugin' => $this->_plugin, 'theme' => $this->_theme, 'layout' => $this->_layout, 'autoLayout' => $this->_autoLayout, 'layoutPath' => $this->_layoutPath, 'helpers' => $this->_helpers, 'viewVars' => $vars, ]; $data += $this->_options; return new $className($request, $response, $events, $data); }
builderのbuild()はViewVarsTraitのcreateView($viewClass = null)で呼ばれている。
ViewVarsTraitはControllerで使われている。
Controllerのrender($view = null, $layout = null)関数。
public function render($view = null, $layout = null) { // ビュービルダーを取得 $builder = $this->viewBuilder(); // ビルダーにテンプレートパスが設定されてないならコントローラの_viewPath()で設定する。 if (!$builder->getTemplatePath()) { $builder->setTemplatePath($this->_viewPath()); } // リクエストにbareパラムがあるならAutoLayoutを切る if ($this->request->getParam('bare')) { $builder->enableAutoLayout(false); } $builder->getClassName($this->viewClass); // コントローラのautoRenderを切る $this->autoRender = false; // beforeRenderイベントを通知 $event = $this->dispatchEvent('Controller.beforeRender'); if ($event->getResult() instanceof Response) { return $event->getResult(); } if ($event->isStopped()) { return $this->response; } // ビルダーにテンプレートが設定されなく、リクエストにアクションが設定されているなら、そのアクションからテンプレートを引っ張ってきて設定する。 if ($builder->getTemplate() === null && $this->request->getParam('action')) { $builder->setTemplate($this->request->getParam('action')); } // ビューを生成する。 $this->View = $this->createView(); // ビューをレイアウトを使ってレンダリングする。 $this->response->body($this->View->render($view, $layout)); return $this->response; }
renderはControllerのactionで明示的に呼ぶこともできるがautoRenderを設定しているときはactionが呼ばれたときに自動的に呼ばれている。その機構はActionDispatcherの_invoke(Controller $controller)にある。
protected function _invoke(Controller $controller) { // invokeControllerイベントを通知する。 $this->dispatchEvent('Dispatcher.invokeController', ['controller' => $controller]); $result = $controller->startupProcess(); if ($result instanceof Response) { return $result; } // コントローラのinvokeActionを呼ぶ ここでコントローラのアクションが呼ばれる。 $response = $controller->invokeAction(); // アクションからのレスポンスがなくてautoRenderがtrueならレンダリングしてそのresponseを返却する。 if ($response !== null && !($response instanceof Response)) { throw new LogicException('Controller actions can only return Cake\Http\Response or null.'); } if (!$response && $controller->autoRender) { $response = $controller->render(); } elseif (!$response) { $response = $controller->response; } $result = $controller->shutdownProcess(); if ($result instanceof Response) { return $result; } return $response; }
この_invoke関数はdispatch(ServerRequest $request, Response $response)関数から呼ばれる。
public function dispatch(ServerRequest $request, Response $response) { if (Router::getRequest(true) !== $request) { Router::pushRequest($request); } // beforeDispatchを通知 $beforeEvent = $this->dispatchEvent('Dispatcher.beforeDispatch', compact('request', 'response')); $request = $beforeEvent->getData('request'); if ($beforeEvent->getResult() instanceof Response) { return $beforeEvent->getResult(); } // コントローラを生成しているらしい。。 // Use the controller built by an beforeDispatch // event handler if there is one. if ($beforeEvent->getData('controller') instanceof Controller) { $controller = $beforeEvent->getData('controller'); } else { $controller = $this->factory->create($request, $response); } // 生成したコントローラに対して_invokeを実行。 $response = $this->_invoke($controller); // requestにreturnが設定されていたらresponseを返却 if (isset($request->params['return'])) { return $response; } // afterDispatchを通知 $afterEvent = $this->dispatchEvent('Dispatcher.afterDispatch', compact('request', 'response')); // return $afterEvent->getData('response'); }
このdispatchはDispatcherのdispatchで呼ばれる。
public function dispatch(ServerRequest $request, Response $response) { $actionDispatcher = new ActionDispatcher(null, $this->eventManager(), $this->_filters); $response = $actionDispatcher->dispatch($request, $response); if (isset($request->params['return'])) { return $response->body(); } return $response->send(); }
DispatcherはDispatcherFactoryのcreate関数で作られる。
public static function create()
{
$dispatcher = new Dispatcher();
foreach (static::$_stack as $middleware) {
$dispatcher->addFilter($middleware);
}
return $dispatcher;
}
アクションがどのように呼ばれるのか
このあたりで、CakePHPでどのようにアクションが呼ばれるのかについて調べていることを自覚した。
webrootのindex.phoを調べてみる。
// Bind your application to the server. $server = new Server(new Application(dirname(__DIR__) . '/config')); // Run the request/response through the application // and emit the response. $server->emit($server->run());
このあたりがポイントか。このApplicationはBaseApplicationを継承している。BaseApplicationではgetDispatcherでActionDispatcherを作り、__invokeでdispatchしている。
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, $next) { return $this->getDispatcher()->dispatch($request, $response); }
というわけで
BaseApplicationの__invokeでActionDispatcherがdispatchし、その関数内でコントローラに_invokeする。
_invokeはautoRenderがtrueだとコントローラのrender()関数を呼び、render()関数はビュービルダーを使ってビューをビルドする。という流れのようだ。
Server
サーバは以下の方法でRunnerにApplicationとミドルウェアを追加してrunし、Runnerは__invokeして回るのでリクエストが処理されていくようだ。
public function run(ServerRequestInterface $request = null, ResponseInterface $response = null) { $this->app->bootstrap(); $response = $response ?: new Response(); // リクエスト取得 $request = $request ?: ServerRequestFactory::fromGlobals(); // ミドルウェア $middleware = $this->app->middleware(new MiddlewareQueue()); if (!($middleware instanceof MiddlewareQueue)) { throw new RuntimeException('The application `middleware` method did not return a middleware queue.'); } // ミドルウェア生成イベント通知 $this->dispatchEvent('Server.buildMiddleware', ['middleware' => $middleware]); // まずミドルウェアとしてApplicationを登録 $middleware->add($this->app); // runnerにrunしレスポンスを取得 $response = $this->runner->run($middleware, $request, $response); if (!($response instanceof ResponseInterface)) { throw new RuntimeException(sprintf( 'Application did not create a response. Got "%s" instead.', is_object($response) ? get_class($response) : $response )); } // レスポンスを返却 return $response; }
このrunはMiddlewareDispatcherにてexecute($request)されるようだ。
public function execute($request) { try { $reflect = new ReflectionClass($this->_class); $app = $reflect->newInstanceArgs($this->_constructorArgs); } catch (ReflectionException $e) { throw new LogicException(sprintf( 'Cannot load "%s" for use in integration testing.', $this->_class )); } // Spy on the controller using the initialize hook instead // of the dispatcher hooks as those will be going away one day. EventManager::instance()->on( 'Controller.initialize', [$this->_test, 'controllerSpy'] ); $server = new Server($app); $psrRequest = $this->_createRequest($request); return $server->run($psrRequest); }
View
AppViewが基底でViewを継承している。
自作のヘルパーなどはinitialize内で $this->loadHelper('MyUtils');してやることで使えるようになる。
View
EventDispatcherInterfaceを実装している。
使用しているtrait
CellTrait
EventDispatcherTrait;
LogTrait;
RequestActionTrait;
ViewVarsTrait;
変数
_helpers
Blocks
plugin
name ビューを作ったコントローラの名前
helpers ビルトインされているヘルパー
templatePath テンプレートのパス
template テンプレートの名前
layout 使用しているレイアウト
layoutPath 使用しているレイアウトの名前
autoLayout 自動レイアウトのonoff
_ext テンプレートの拡張子 デフォルトはctp
subDir
theme ビューのテーマ
hasRendered ビューがすでにレンダリングされているか
uuids 生成されたDOMのuuid
request
response
elementCache
_passedVars 関連するコントローラからの変数
_paths
_pathsForPlugin
_parents ビューの継承元。View::extend()で使用。
_current
_currentType
_stack ネストされたテンプレート。View::extend()で使用。
TYPE_TEMPLATE
TYPE_ELEMENT
TYPE_LAYOUT
メソッド
initialize()
templatePath($path = null) テンプレートのセットとゲット
layoutPath($path = null) レイアウトのセットとゲット
autoLayout($autoLayout = null)
theme($theme = null)
template($name = null)
layout($name = null)
element($name, array $data = , array $options = ) エレメントの描画。_renderElementを使う。
cache(callable $block, array $options = )
elementExists($name) エレメントがあるかのチェック
render($view = null, $layout = null) 与えられたテンプレートとレイアウトでレンダリングする。
renderLayout($content, $layout = null) レイアウトのレンダリング
getVars() $this->viewVarsの名前を返す
get($var, $default = null) view変数を取得
blocks() ビューブロックのキーを返す。$this->Blocks->keys();
start($name) ビューブロックのスタート $this->Blocks->start($name);
append($name, $value = null) ビューブロックの追加 $this->Blocks->concat($name, $value);
prepend($name, $value) $this->Blocks->concat($name, $value, ViewBlock::PREPEND);
assign($name, $value) ビューブロックのセット $this->Blocks->set($name, $value);
reset($name) ビューブロックのリセット $this->assign($name, '');
fetch($name, $default = '') ビューブロックのコンテンツを取得 $this->Blocks->get($name, $default);
end() ビューブロックのエンド $this->Blocks->end();
exists($name) ビューブロックの存在確認 $this->Blocks->exists($name);
extend($name) テンプレートの継承
uuid($object, $url) uuidの取得
getCurrentType()
__get($name)
__set($name, $value)
loadHelpers() ヘルパーの読み込み
_render($viewFile, $data = ) レンダリング
_evaluate($viewFile, $dataForView)
helpers() ヘルパー
loadHelper($name, array $config = []) ヘルパーの読み込み
_getViewFileName($name = null)
_inflectViewFileName($name)
_checkFilePath($file, $path)
pluginSplit($name, $fallback = true)
_getLayoutFileName($name = null)
_getElementFileName($name, $pluginCheck = true)
_getSubPaths($basePath)
_paths($plugin = null, $cached = true)
_elementCache($name, $data, $options)
_renderElement($file, $data, $options)