diff --git a/resources/sql/patches/021.xhpastview.sql b/resources/sql/patches/021.xhpastview.sql new file mode 100644 index 0000000000..09ec1b16f0 --- /dev/null +++ b/resources/sql/patches/021.xhpastview.sql @@ -0,0 +1,9 @@ +CREATE DATABASE phabricator_xhpastview; +CREATE TABLE phabricator_xhpastview.xhpastview_parsetree ( + id int unsigned not null auto_increment primary key, + authorPHID varchar(64) binary, + input longblob not null, + stdout longblob not null, + dateCreated int unsigned not null, + dateModified int unsigned not null +); \ No newline at end of file diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 860271769b..3ad9d4c2c8 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -423,6 +423,16 @@ phutil_register_library_map(array( 'PhabricatorWorkerTask' => 'infrastructure/daemon/workers/storage/task', 'PhabricatorWorkerTaskData' => 'infrastructure/daemon/workers/storage/taskdata', 'PhabricatorWorkerTaskDetailController' => 'applications/daemon/controller/workertaskdetail', + 'PhabricatorXHPASTViewController' => 'applications/xhpastview/controller/base', + 'PhabricatorXHPASTViewDAO' => 'applications/xhpastview/storage/base', + 'PhabricatorXHPASTViewFrameController' => 'applications/xhpastview/controller/viewframe', + 'PhabricatorXHPASTViewFramesetController' => 'applications/xhpastview/controller/viewframeset', + 'PhabricatorXHPASTViewInputController' => 'applications/xhpastview/controller/viewinput', + 'PhabricatorXHPASTViewPanelController' => 'applications/xhpastview/controller/viewpanel', + 'PhabricatorXHPASTViewParseTree' => 'applications/xhpastview/storage/parsetree', + 'PhabricatorXHPASTViewRunController' => 'applications/xhpastview/controller/run', + 'PhabricatorXHPASTViewStreamController' => 'applications/xhpastview/controller/viewstream', + 'PhabricatorXHPASTViewTreeController' => 'applications/xhpastview/controller/viewtree', 'PhabricatorXHProfController' => 'applications/xhprof/controller/base', 'PhabricatorXHProfProfileController' => 'applications/xhprof/controller/profile', 'PhabricatorXHProfProfileSymbolView' => 'applications/xhprof/view/symbol', @@ -776,6 +786,16 @@ phutil_register_library_map(array( 'PhabricatorWorkerTask' => 'PhabricatorWorkerDAO', 'PhabricatorWorkerTaskData' => 'PhabricatorWorkerDAO', 'PhabricatorWorkerTaskDetailController' => 'PhabricatorDaemonController', + 'PhabricatorXHPASTViewController' => 'PhabricatorController', + 'PhabricatorXHPASTViewDAO' => 'PhabricatorLiskDAO', + 'PhabricatorXHPASTViewFrameController' => 'PhabricatorXHPASTViewController', + 'PhabricatorXHPASTViewFramesetController' => 'PhabricatorXHPASTViewController', + 'PhabricatorXHPASTViewInputController' => 'PhabricatorXHPASTViewPanelController', + 'PhabricatorXHPASTViewPanelController' => 'PhabricatorXHPASTViewController', + 'PhabricatorXHPASTViewParseTree' => 'PhabricatorXHPASTViewDAO', + 'PhabricatorXHPASTViewRunController' => 'PhabricatorXHPASTViewController', + 'PhabricatorXHPASTViewStreamController' => 'PhabricatorXHPASTViewPanelController', + 'PhabricatorXHPASTViewTreeController' => 'PhabricatorXHPASTViewPanelController', 'PhabricatorXHProfController' => 'PhabricatorController', 'PhabricatorXHProfProfileController' => 'PhabricatorXHProfController', 'PhabricatorXHProfProfileSymbolView' => 'AphrontView', diff --git a/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php b/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php index dd75e97589..6264d31a51 100644 --- a/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php +++ b/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php @@ -268,6 +268,20 @@ class AphrontDefaultApplicationConfiguration 'delete/(?P\d+)/$' => 'PhabricatorOwnersDeleteController', ), + '/xhpast/' => array( + '$' => 'PhabricatorXHPASTViewRunController', + 'view/(?P\d+)/$' + => 'PhabricatorXHPASTViewFrameController', + 'frameset/(?P\d+)/$' + => 'PhabricatorXHPASTViewFramesetController', + 'input/(?P\d+)/$' + => 'PhabricatorXHPASTViewInputController', + 'tree/(?P\d+)/$' + => 'PhabricatorXHPASTViewTreeController', + 'stream/(?P\d+)/$' + => 'PhabricatorXHPASTViewStreamController', + ), + ); } diff --git a/src/aphront/response/webpage/AphrontWebpageResponse.php b/src/aphront/response/webpage/AphrontWebpageResponse.php index c30ebdcad3..9c29d90d31 100644 --- a/src/aphront/response/webpage/AphrontWebpageResponse.php +++ b/src/aphront/response/webpage/AphrontWebpageResponse.php @@ -22,21 +22,30 @@ class AphrontWebpageResponse extends AphrontResponse { private $content; + private $frameable; public function setContent($content) { $this->content = $content; return $this; } + public function setFrameable($frameable) { + $this->frameable = $frameable; + return $this; + } + public function buildResponseString() { return $this->content; } public function getHeaders() { - return array( - array('Content-Type', 'text/html; charset=UTF-8'), - array('X-Frame-Options', 'Deny'), + $headers = array( + array('Content-Type', 'text/html; charset=UTF-8'), ); + if (!$this->frameable) { + $headers[] = array('X-Frame-Options', 'Deny'); + } + return $headers; } } diff --git a/src/applications/xhpastview/controller/base/PhabricatorXHPASTViewController.php b/src/applications/xhpastview/controller/base/PhabricatorXHPASTViewController.php new file mode 100644 index 0000000000..12a60fa7d7 --- /dev/null +++ b/src/applications/xhpastview/controller/base/PhabricatorXHPASTViewController.php @@ -0,0 +1,35 @@ +buildStandardPageView(); + + $page->setApplicationName('XHPASTView'); + $page->setBaseURI('/xhpast/'); + $page->setTitle(idx($data, 'title')); + $page->setGlyph("\xE2\x96\xA0"); + $page->appendChild($view); + + $response = new AphrontWebpageResponse(); + return $response->setContent($page->render()); + } + +} diff --git a/src/applications/xhpastview/controller/base/__init__.php b/src/applications/xhpastview/controller/base/__init__.php new file mode 100644 index 0000000000..24ef441d7c --- /dev/null +++ b/src/applications/xhpastview/controller/base/__init__.php @@ -0,0 +1,15 @@ +getRequest(); + $user = $request->getUser(); + + if ($request->isFormPost()) { + $source = $request->getStr('source'); + + $future = xhpast_get_parser_future($source); + $resolved = $future->resolve(); + + // This is just to let it throw exceptions if stuff is broken. + $parse_tree = XHPASTTree::newFromDataAndResolvedExecFuture( + $source, + $resolved); + + list($err, $stdout, $stderr) = $resolved; + + $storage_tree = new PhabricatorXHPASTViewParseTree(); + $storage_tree->setInput($source); + $storage_tree->setStdout($stdout); + $storage_tree->setAuthorPHID($user->getPHID()); + $storage_tree->save(); + + return id(new AphrontRedirectResponse()) + ->setURI('/xhpast/view/'.$storage_tree->getID().'/'); + } + + $form = id(new AphrontFormView()) + ->setUser($user) + ->appendChild( + id(new AphrontFormTextAreaControl()) + ->setLabel('Source') + ->setName('source') + ->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_TALL)) + ->appendChild( + id(new AphrontFormSubmitControl()) + ->setValue('Parse')); + + $panel = new AphrontPanelView(); + $panel->setHeader('Generate XHP AST'); + $panel->setWidth(AphrontPanelView::WIDTH_WIDE); + $panel->appendChild($form); + + return $this->buildStandardPageResponse( + $panel, + array( + 'title' => 'XHPAST View', + )); + } + +} diff --git a/src/applications/xhpastview/controller/run/__init__.php b/src/applications/xhpastview/controller/run/__init__.php new file mode 100644 index 0000000000..b8cd348573 --- /dev/null +++ b/src/applications/xhpastview/controller/run/__init__.php @@ -0,0 +1,21 @@ +id = $data['id']; + } + + public function processRequest() { + $id = $this->id; + + return $this->buildStandardPageResponse( + phutil_render_tag( + 'iframe', + array( + 'src' => '/xhpast/frameset/'.$id.'/', + 'frameborder' => '0', + 'style' => 'width: 100%; height: 800px;', + '')), + array( + 'title' => 'XHPAST View', + )); + } +} diff --git a/src/applications/xhpastview/controller/viewframe/__init__.php b/src/applications/xhpastview/controller/viewframe/__init__.php new file mode 100644 index 0000000000..64fc5e108f --- /dev/null +++ b/src/applications/xhpastview/controller/viewframe/__init__.php @@ -0,0 +1,14 @@ +id = $data['id']; + } + + public function processRequest() { + $id = $this->id; + + $response = new AphrontWebpageResponse(); + $response->setFrameable(true); + $response->setContent( + ''. + ''. + ''. + ''. + ''); + + return $response; + } +} diff --git a/src/applications/xhpastview/controller/viewframeset/__init__.php b/src/applications/xhpastview/controller/viewframeset/__init__.php new file mode 100644 index 0000000000..c9fcc90ee7 --- /dev/null +++ b/src/applications/xhpastview/controller/viewframeset/__init__.php @@ -0,0 +1,13 @@ +getStorageTree()->getInput(); + return $this->buildXHPASTViewPanelResponse( + phutil_escape_html($input)); + } +} diff --git a/src/applications/xhpastview/controller/viewinput/__init__.php b/src/applications/xhpastview/controller/viewinput/__init__.php new file mode 100644 index 0000000000..95153c75f8 --- /dev/null +++ b/src/applications/xhpastview/controller/viewinput/__init__.php @@ -0,0 +1,14 @@ +id = $data['id']; + $this->storageTree = id(new PhabricatorXHPASTViewParseTree()) + ->load($this->id); + if (!$this->storageTree) { + throw new Exception("No such AST!"); + } + } + + protected function getStorageTree() { + return $this->storageTree; + } + + protected function buildXHPASTViewPanelResponse($content) { + $content = + ''. + ''. + ''. + ''. + ''. + ''. + $content. + ''. + ''; + + $response = new AphrontWebpageResponse(); + $response->setFrameable(true); + $response->setContent($content); + return $response; + } + +} diff --git a/src/applications/xhpastview/controller/viewpanel/__init__.php b/src/applications/xhpastview/controller/viewpanel/__init__.php new file mode 100644 index 0000000000..f6cf718b9d --- /dev/null +++ b/src/applications/xhpastview/controller/viewpanel/__init__.php @@ -0,0 +1,16 @@ +getStorageTree(); + $input = $storage->getInput(); + $stdout = $storage->getStdout(); + + $tree = XHPASTTree::newFromDataAndResolvedExecFuture( + $input, + array(0, $stdout, '')); + + $tokens = array(); + foreach ($tree->getRawTokenStream() as $id => $token) { + $seq = $id; + $name = $token->getTypeName(); + $title = "Token {$seq}: {$name}"; + + $tokens[] = phutil_render_tag( + 'span', + array( + 'title' => $title, + 'class' => 'token', + ), + phutil_escape_html($token->getValue())); + } + + return $this->buildXHPASTViewPanelResponse(implode('', $tokens)); + } +} diff --git a/src/applications/xhpastview/controller/viewstream/__init__.php b/src/applications/xhpastview/controller/viewstream/__init__.php new file mode 100644 index 0000000000..de768b5532 --- /dev/null +++ b/src/applications/xhpastview/controller/viewstream/__init__.php @@ -0,0 +1,15 @@ +getStorageTree(); + $input = $storage->getInput(); + $stdout = $storage->getStdout(); + + $tree = XHPASTTree::newFromDataAndResolvedExecFuture( + $input, + array(0, $stdout, '')); + + $tree = '
    '.$this->buildTree($tree->getRootNode()).'
'; + return $this->buildXHPASTViewPanelResponse($tree); + } + + protected function buildTree($root) { + + try { + $name = $root->getTypeName(); + $title = $root->getDescription(); + } catch (Exception $ex) { + $name = '???'; + $title = '???'; + } + + $tree = array(); + $tree[] = + '
  • '. + phutil_render_tag( + 'span', + array( + 'title' => $title, + ), + phutil_escape_html($name)). + '
  • '; + foreach ($root->getChildren() as $child) { + $tree[] = '
      '.$this->buildTree($child).'
    '; + } + return implode("\n", $tree); + } + +} diff --git a/src/applications/xhpastview/controller/viewtree/__init__.php b/src/applications/xhpastview/controller/viewtree/__init__.php new file mode 100644 index 0000000000..847f53a70d --- /dev/null +++ b/src/applications/xhpastview/controller/viewtree/__init__.php @@ -0,0 +1,15 @@ +getMessage());