Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F2894509
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Advanced/Developer...
View Handle
View Hovercard
Size
30 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/applications/auth/view/PhabricatorOAuthFailureView.php b/src/applications/auth/view/PhabricatorOAuthFailureView.php
index b8fa2afd2f..a1e4549745 100644
--- a/src/applications/auth/view/PhabricatorOAuthFailureView.php
+++ b/src/applications/auth/view/PhabricatorOAuthFailureView.php
@@ -1,88 +1,89 @@
<?php
final class PhabricatorOAuthFailureView extends AphrontView {
private $request;
private $provider;
private $exception;
public function setRequest(AphrontRequest $request) {
$this->request = $request;
return $this;
}
public function setOAuthProvider($provider) {
$this->provider = $provider;
return $this;
}
public function setException(Exception $e) {
$this->exception = $e;
return $this;
}
public function render() {
$request = $this->request;
$provider = $this->provider;
$provider_name = $provider->getProviderName();
$diagnose = null;
$view = new AphrontRequestFailureView();
$view->setHeader(pht('%s Auth Failed', $provider_name));
if ($this->request) {
$view->appendChild(
hsprintf(
'<p><strong>Description:</strong> %s</p>',
$request->getStr('error_description')));
$view->appendChild(
hsprintf(
'<p><strong>Error:</strong> %s</p>',
$request->getStr('error')));
$view->appendChild(
hsprintf(
'<p><strong>Error Reason:</strong> %s</p>',
$request->getStr('error_reason')));
} else if ($this->exception) {
$view->appendChild(
hsprintf(
'<p><strong>Error Details:</strong> %s</p>',
$this->exception->getMessage()));
} else {
// TODO: We can probably refine this.
$view->appendChild(
hsprintf(
'<p>Unable to authenticate with %s. '.
'There are several reasons this might happen:</p>'.
'<ul>'.
'<li>Phabricator may be configured with the wrong Application '.
'Secret; or</li>'.
'<li>the %s OAuth access token may have expired; or</li>'.
'<li>%s may have revoked authorization for the Application; '.
'or</li>'.
'<li>%s may be having technical problems.</li>'.
'</ul>'.
'<p>You can try again, or login using another method.</p>',
$provider_name,
$provider_name,
$provider_name,
$provider_name));
$provider_key = $provider->getProviderKey();
$diagnose = hsprintf(
- '<a href="/oauth/'.$provider_key.'/diagnose/" class="button green">'.
+ '<a href="/oauth/%s/diagnose/" class="button green">'.
'Diagnose %s OAuth Problems'.
'</a>',
+ $provider_key,
$provider_name);
}
$view->appendChild(
'<div class="aphront-failure-continue">'.
$diagnose.
'<a href="/login/" class="button">'.pht('Continue').'</a>'.
'</div>');
return $view->render();
}
}
diff --git a/src/applications/diffusion/view/DiffusionBrowseTableView.php b/src/applications/diffusion/view/DiffusionBrowseTableView.php
index 004faf0a21..a7ada70a8e 100644
--- a/src/applications/diffusion/view/DiffusionBrowseTableView.php
+++ b/src/applications/diffusion/view/DiffusionBrowseTableView.php
@@ -1,289 +1,288 @@
<?php
final class DiffusionBrowseTableView extends DiffusionView {
private $paths;
private $handles = array();
public function setPaths(array $paths) {
assert_instances_of($paths, 'DiffusionRepositoryPath');
$this->paths = $paths;
return $this;
}
public function setHandles(array $handles) {
assert_instances_of($handles, 'PhabricatorObjectHandle');
$this->handles = $handles;
return $this;
}
public function renderLastModifiedColumns(
array $handles,
PhabricatorRepositoryCommit $commit = null,
PhabricatorRepositoryCommitData $data = null) {
assert_instances_of($handles, 'PhabricatorObjectHandle');
$drequest = $this->getDiffusionRequest();
if ($commit) {
$epoch = $commit->getEpoch();
$modified = DiffusionView::linkCommit(
$drequest->getRepository(),
$commit->getCommitIdentifier());
$date = phabricator_date($epoch, $this->user);
$time = phabricator_time($epoch, $this->user);
} else {
$modified = '';
$date = '';
$time = '';
}
if ($data) {
$author_phid = $data->getCommitDetail('authorPHID');
if ($author_phid && isset($handles[$author_phid])) {
$author = $handles[$author_phid]->renderLink();
} else {
$author = self::renderName($data->getAuthorName());
}
$committer = $data->getCommitDetail('committer');
if ($committer) {
$committer_phid = $data->getCommitDetail('committerPHID');
if ($committer_phid && isset($handles[$committer_phid])) {
$committer = $handles[$committer_phid]->renderLink();
} else {
$committer = self::renderName($committer);
}
if ($author != $committer) {
$author .= '/'.$committer;
}
}
$details = AphrontTableView::renderSingleDisplayLine(
phutil_escape_html($data->getSummary()));
} else {
$author = '';
$details = '';
}
$return = array(
'commit' => $modified,
'date' => $date,
'time' => $time,
'author' => $author,
'details' => $details,
);
$lint = self::loadLintMessagesCount($drequest);
if ($lint !== null) {
$return['lint'] = hsprintf(
'<a href="%s">%s</a>',
$drequest->generateURI(array(
'action' => 'lint',
'lint' => null,
)),
number_format($lint));
}
return $return;
}
private static function loadLintMessagesCount(DiffusionRequest $drequest) {
$branch = $drequest->loadBranch();
if (!$branch) {
return null;
}
$conn = $drequest->getRepository()->establishConnection('r');
- $where = '';
+ $path = '/'.$drequest->getPath();
+ $where = (substr($path, -1) == '/'
+ ? qsprintf($conn, 'AND path LIKE %>', $path)
+ : qsprintf($conn, 'AND path = %s', $path));
+
if ($drequest->getLint()) {
- $where = qsprintf(
- $conn,
- 'AND code = %s',
- $drequest->getLint());
+ $where .= qsprintf($conn, ' AND code = %s', $drequest->getLint());
}
- $like = (substr($drequest->getPath(), -1) == '/' ? 'LIKE %>' : '= %s');
return head(queryfx_one(
$conn,
- 'SELECT COUNT(*) FROM %T WHERE branchID = %d %Q AND path '.$like,
+ 'SELECT COUNT(*) FROM %T WHERE branchID = %d %Q',
PhabricatorRepository::TABLE_LINTMESSAGE,
$branch->getID(),
- $where,
- '/'.$drequest->getPath()));
+ $where));
}
public function render() {
$request = $this->getDiffusionRequest();
$repository = $request->getRepository();
$base_path = trim($request->getPath(), '/');
if ($base_path) {
$base_path = $base_path.'/';
}
$need_pull = array();
$rows = array();
$show_edit = false;
foreach ($this->paths as $path) {
$dir_slash = null;
$file_type = $path->getFileType();
if ($file_type == DifferentialChangeType::FILE_DIRECTORY) {
$browse_text = $path->getPath().'/';
$dir_slash = '/';
$browse_link = '<strong>'.$this->linkBrowse(
$base_path.$path->getPath().$dir_slash,
array(
'html' => $this->renderPathIcon(
'dir',
$browse_text),
)).'</strong>';
} else if ($file_type == DifferentialChangeType::FILE_SUBMODULE) {
$browse_text = $path->getPath().'/';
$browse_link =
'<strong>'.
$this->linkExternal(
$path->getHash(),
$path->getExternalURI(),
$this->renderPathIcon(
'ext',
$browse_text)).
'</strong>';
} else {
if ($file_type == DifferentialChangeType::FILE_SYMLINK) {
$type = 'link';
} else {
$type = 'file';
}
$browse_text = $path->getPath();
$browse_link = $this->linkBrowse(
$base_path.$path->getPath(),
array(
'html' => $this->renderPathIcon($type, $browse_text),
));
}
$commit = $path->getLastModifiedCommit();
if ($commit) {
$drequest = clone $request;
$drequest->setPath($request->getPath().$path->getPath().$dir_slash);
$dict = $this->renderLastModifiedColumns(
$this->handles,
$commit,
$path->getLastCommitData());
} else {
$dict = array(
'lint' => celerity_generate_unique_node_id(),
'commit' => celerity_generate_unique_node_id(),
'date' => celerity_generate_unique_node_id(),
'time' => celerity_generate_unique_node_id(),
'author' => celerity_generate_unique_node_id(),
'details' => celerity_generate_unique_node_id(),
);
$uri = (string)$request->generateURI(
array(
'action' => 'lastmodified',
'path' => $base_path.$path->getPath().$dir_slash,
));
$need_pull[$uri] = $dict;
foreach ($dict as $k => $uniq) {
$dict[$k] = '<span id="'.$uniq.'"></span>';
}
}
$editor_button = '';
if ($this->user) {
$editor_link = $this->user->loadEditorLink(
$base_path.$path->getPath(),
1,
$request->getRepository()->getCallsign());
if ($editor_link) {
$show_edit = true;
$editor_button = phutil_render_tag(
'a',
array(
'href' => $editor_link,
),
'Edit');
}
}
$rows[] = array(
$this->linkHistory($base_path.$path->getPath().$dir_slash),
$editor_button,
$browse_link,
idx($dict, 'lint'),
$dict['commit'],
$dict['date'],
$dict['time'],
$dict['author'],
$dict['details'],
);
}
if ($need_pull) {
Javelin::initBehavior('diffusion-pull-lastmodified', $need_pull);
}
$branch = $this->getDiffusionRequest()->loadBranch();
$show_lint = ($branch && $branch->getLintCommit());
$lint = $request->getLint();
$view = new AphrontTableView($rows);
$view->setHeaders(
array(
'History',
'Edit',
'Path',
($lint ? phutil_escape_html($lint) : 'Lint'),
'Modified',
'Date',
'Time',
'Author/Committer',
'Details',
));
$view->setColumnClasses(
array(
'',
'',
'',
'n',
'',
'',
'right',
'',
'wide',
));
$view->setColumnVisibility(
array(
true,
$show_edit,
true,
$show_lint,
true,
true,
true,
true,
true,
));
return $view->render();
}
private function renderPathIcon($type, $text) {
require_celerity_resource('diffusion-icons-css');
return phutil_render_tag(
'span',
array(
'class' => 'diffusion-path-icon diffusion-path-icon-'.$type,
),
phutil_escape_html($text));
}
}
diff --git a/src/applications/search/engine/PhabricatorSearchEngineMySQL.php b/src/applications/search/engine/PhabricatorSearchEngineMySQL.php
index cdc95be54e..65f3b6b4da 100644
--- a/src/applications/search/engine/PhabricatorSearchEngineMySQL.php
+++ b/src/applications/search/engine/PhabricatorSearchEngineMySQL.php
@@ -1,320 +1,321 @@
<?php
/**
* @group search
*/
final class PhabricatorSearchEngineMySQL extends PhabricatorSearchEngine {
public function reindexAbstractDocument(
PhabricatorSearchAbstractDocument $doc) {
$phid = $doc->getPHID();
if (!$phid) {
throw new Exception("Document has no PHID!");
}
$store = new PhabricatorSearchDocument();
$store->setPHID($doc->getPHID());
$store->setDocumentType($doc->getDocumentType());
$store->setDocumentTitle($doc->getDocumentTitle());
$store->setDocumentCreated($doc->getDocumentCreated());
$store->setDocumentModified($doc->getDocumentModified());
$store->replace();
$conn_w = $store->establishConnection('w');
$field_dao = new PhabricatorSearchDocumentField();
queryfx(
$conn_w,
'DELETE FROM %T WHERE phid = %s',
$field_dao->getTableName(),
$phid);
foreach ($doc->getFieldData() as $field) {
list($ftype, $corpus, $aux_phid) = $field;
queryfx(
$conn_w,
'INSERT INTO %T (phid, phidType, field, auxPHID, corpus) '.
' VALUES (%s, %s, %s, %ns, %s)',
$field_dao->getTableName(),
$phid,
$doc->getDocumentType(),
$ftype,
$aux_phid,
$corpus);
}
$sql = array();
foreach ($doc->getRelationshipData() as $relationship) {
list($rtype, $to_phid, $to_type, $time) = $relationship;
$sql[] = qsprintf(
$conn_w,
'(%s, %s, %s, %s, %d)',
$phid,
$to_phid,
$rtype,
$to_type,
$time);
}
$rship_dao = new PhabricatorSearchDocumentRelationship();
queryfx(
$conn_w,
'DELETE FROM %T WHERE phid = %s',
$rship_dao->getTableName(),
$phid);
if ($sql) {
queryfx(
$conn_w,
'INSERT INTO %T'.
' (phid, relatedPHID, relation, relatedType, relatedTime) '.
' VALUES %Q',
$rship_dao->getTableName(),
implode(', ', $sql));
}
}
/**
* Rebuild the PhabricatorSearchAbstractDocument that was used to index
* an object out of the index itself. This is primarily useful for debugging,
* as it allows you to inspect the search index representation of a
* document.
*
* @param phid PHID of a document which exists in the search index.
* @return null|PhabricatorSearchAbstractDocument Abstract document object
* which corresponds to the original abstract document used to
* build the document index.
*/
public function reconstructDocument($phid) {
$dao_doc = new PhabricatorSearchDocument();
$dao_field = new PhabricatorSearchDocumentField();
$dao_relationship = new PhabricatorSearchDocumentRelationship();
$t_doc = $dao_doc->getTableName();
$t_field = $dao_field->getTableName();
$t_relationship = $dao_relationship->getTableName();
$doc = queryfx_one(
$dao_doc->establishConnection('r'),
'SELECT * FROM %T WHERE phid = %s',
$t_doc,
$phid);
if (!$doc) {
return null;
}
$fields = queryfx_all(
$dao_field->establishConnection('r'),
'SELECT * FROM %T WHERE phid = %s',
$t_field,
$phid);
$relationships = queryfx_all(
$dao_relationship->establishConnection('r'),
'SELECT * FROM %T WHERE phid = %s',
$t_relationship,
$phid);
$adoc = id(new PhabricatorSearchAbstractDocument())
->setPHID($phid)
->setDocumentType($doc['documentType'])
->setDocumentTitle($doc['documentTitle'])
->setDocumentCreated($doc['documentCreated'])
->setDocumentModified($doc['documentModified']);
foreach ($fields as $field) {
$adoc->addField(
$field['field'],
$field['corpus'],
$field['auxPHID']);
}
foreach ($relationships as $relationship) {
$adoc->addRelationship(
$relationship['relation'],
$relationship['relatedPHID'],
$relationship['relatedType'],
$relationship['relatedTime']);
}
return $adoc;
}
public function executeSearch(PhabricatorSearchQuery $query) {
$where = array();
$join = array();
$order = 'ORDER BY documentCreated DESC';
$dao_doc = new PhabricatorSearchDocument();
$dao_field = new PhabricatorSearchDocumentField();
$t_doc = $dao_doc->getTableName();
$t_field = $dao_field->getTableName();
$conn_r = $dao_doc->establishConnection('r');
$q = $query->getQuery();
if (strlen($q)) {
$join[] = qsprintf(
$conn_r,
- "{$t_field} field ON field.phid = document.phid");
+ '%T field ON field.phid = document.phid',
+ $t_field);
$where[] = qsprintf(
$conn_r,
'MATCH(corpus) AGAINST (%s IN BOOLEAN MODE)',
$q);
// When searching for a string, promote user listings above other
// listings.
$order = qsprintf(
$conn_r,
'ORDER BY
IF(documentType = %s, 0, 1) ASC,
MAX(MATCH(corpus) AGAINST (%s)) DESC',
'USER',
$q);
$field = $query->getParameter('field');
if ($field/* && $field != AdjutantQuery::FIELD_ALL*/) {
$where[] = qsprintf(
$conn_r,
'field.field = %s',
$field);
}
}
$exclude = $query->getParameter('exclude');
if ($exclude) {
$where[] = qsprintf($conn_r, 'document.phid != %s', $exclude);
}
if ($query->getParameter('type')) {
if (strlen($q)) {
// TODO: verify that this column actually does something useful in query
// plans once we have nontrivial amounts of data.
$where[] = qsprintf(
$conn_r,
'field.phidType = %s',
$query->getParameter('type'));
}
$where[] = qsprintf(
$conn_r,
'document.documentType = %s',
$query->getParameter('type'));
}
$join[] = $this->joinRelationship(
$conn_r,
$query,
'author',
PhabricatorSearchRelationship::RELATIONSHIP_AUTHOR);
$join[] = $this->joinRelationship(
$conn_r,
$query,
'open',
PhabricatorSearchRelationship::RELATIONSHIP_OPEN);
$join[] = $this->joinRelationship(
$conn_r,
$query,
'owner',
PhabricatorSearchRelationship::RELATIONSHIP_OWNER);
$join[] = $this->joinRelationship(
$conn_r,
$query,
'subscribers',
PhabricatorSearchRelationship::RELATIONSHIP_SUBSCRIBER);
$join[] = $this->joinRelationship(
$conn_r,
$query,
'project',
PhabricatorSearchRelationship::RELATIONSHIP_PROJECT);
$join[] = $this->joinRelationship(
$conn_r,
$query,
'repository',
PhabricatorSearchRelationship::RELATIONSHIP_REPOSITORY);
$join = array_filter($join);
foreach ($join as $key => $clause) {
$join[$key] = ' JOIN '.$clause;
}
$join = implode(' ', $join);
if ($where) {
$where = 'WHERE '.implode(' AND ', $where);
} else {
$where = '';
}
$offset = (int)$query->getParameter('offset', 0);
$limit = (int)$query->getParameter('limit', 25);
$hits = queryfx_all(
$conn_r,
'SELECT
document.phid
FROM %T document
%Q
%Q
GROUP BY document.phid
%Q
LIMIT %d, %d',
$t_doc,
$join,
$where,
$order,
$offset,
$limit);
return ipull($hits, 'phid');
}
protected function joinRelationship(
AphrontDatabaseConnection $conn,
PhabricatorSearchQuery $query,
$field,
$type) {
$phids = $query->getParameter($field, array());
if (!$phids) {
return null;
}
$is_existence = false;
switch ($type) {
case PhabricatorSearchRelationship::RELATIONSHIP_OPEN:
$is_existence = true;
break;
}
$sql = qsprintf(
$conn,
'%T AS %C ON %C.phid = document.phid AND %C.relation = %s',
id(new PhabricatorSearchDocumentRelationship())->getTableName(),
$field,
$field,
$field,
$type);
if (!$is_existence) {
$sql .= qsprintf(
$conn,
' AND %C.relatedPHID in (%Ls)',
$field,
$phids);
}
return $sql;
}
}
diff --git a/src/infrastructure/celerity/CelerityResourceTransformer.php b/src/infrastructure/celerity/CelerityResourceTransformer.php
index d650a91b4b..180de4941b 100644
--- a/src/infrastructure/celerity/CelerityResourceTransformer.php
+++ b/src/infrastructure/celerity/CelerityResourceTransformer.php
@@ -1,111 +1,111 @@
<?php
/**
* @group celerity
*/
final class CelerityResourceTransformer {
private $minify;
private $rawResourceMap;
private $celerityMap;
private $translateURICallback;
public function setTranslateURICallback($translate_uricallback) {
$this->translateURICallback = $translate_uricallback;
return $this;
}
public function setMinify($minify) {
$this->minify = $minify;
return $this;
}
public function setRawResourceMap(array $raw_resource_map) {
$this->rawResourceMap = $raw_resource_map;
return $this;
}
public function setCelerityMap(CelerityResourceMap $celerity_map) {
$this->celerityMap = $celerity_map;
return $this;
}
public function transformResource($path, $data) {
$type = self::getResourceType($path);
switch ($type) {
case 'css':
$data = preg_replace_callback(
'@url\s*\((\s*[\'"]?.*?)\)@s',
nonempty(
$this->translateURICallback,
array($this, 'translateResourceURI')),
$data);
break;
}
if (!$this->minify) {
return $data;
}
// Some resources won't survive minification (like Raphael.js), and are
// marked so as not to be minified.
if (strpos($data, '@'.'do-not-minify') !== false) {
return $data;
}
switch ($type) {
case 'css':
// Remove comments.
$data = preg_replace('@/\*.*?\*/@s', '', $data);
// Remove whitespace around symbols.
$data = preg_replace('@\s*([{}:;,])\s*@', '\1', $data);
// Remove unnecessary semicolons.
$data = preg_replace('@;}@', '}', $data);
// Replace #rrggbb with #rgb when possible.
$data = preg_replace(
'@#([a-f0-9])\1([a-f0-9])\2([a-f0-9])\3@i',
'#\1\2\3',
$data);
$data = trim($data);
break;
case 'js':
$root = dirname(phutil_get_library_root('phabricator'));
$bin = $root.'/externals/javelin/support/jsxmin/jsxmin';
if (@file_exists($bin)) {
- $future = new ExecFuture("{$bin} __DEV__:0");
+ $future = new ExecFuture('%s __DEV__:0', $bin);
$future->write($data);
list($err, $result) = $future->resolve();
if (!$err) {
$data = $result;
}
}
break;
}
return $data;
}
public static function getResourceType($path) {
return last(explode('.', $path));
}
public function translateResourceURI(array $matches) {
$uri = trim($matches[1], "'\" \r\t\n");
if ($this->rawResourceMap) {
if (isset($this->rawResourceMap[$uri]['uri'])) {
$uri = $this->rawResourceMap[$uri]['uri'];
}
} else if ($this->celerityMap) {
$info = $this->celerityMap->lookupFileInformation($uri);
if ($info) {
$uri = $info['uri'];
}
}
return 'url('.$uri.')';
}
}
diff --git a/src/infrastructure/lint/linter/PhabricatorJavelinLinter.php b/src/infrastructure/lint/linter/PhabricatorJavelinLinter.php
index e2d39c3680..dc1a09957e 100644
--- a/src/infrastructure/lint/linter/PhabricatorJavelinLinter.php
+++ b/src/infrastructure/lint/linter/PhabricatorJavelinLinter.php
@@ -1,247 +1,245 @@
<?php
final class PhabricatorJavelinLinter extends ArcanistLinter {
private $symbols = array();
private $haveSymbolsBinary;
private $haveWarnedAboutBinary;
const LINT_PRIVATE_ACCESS = 1;
const LINT_MISSING_DEPENDENCY = 2;
const LINT_UNNECESSARY_DEPENDENCY = 3;
const LINT_UNKNOWN_DEPENDENCY = 4;
const LINT_MISSING_BINARY = 5;
public function willLintPaths(array $paths) {
$root = dirname(phutil_get_library_root('phabricator'));
require_once $root.'/scripts/__init_script__.php';
if ($this->haveSymbolsBinary === null) {
list($err) = exec_manual('which javelinsymbols');
$this->haveSymbolsBinary = !$err;
if (!$this->haveSymbolsBinary) {
return;
}
}
$futures = array();
foreach ($paths as $path) {
if ($this->shouldIgnorePath($path)) {
continue;
}
$future = $this->newSymbolsFuture($path);
$futures[$path] = $future;
}
foreach (Futures($futures)->limit(8) as $path => $future) {
$this->symbols[$path] = $future->resolvex();
}
}
public function getLinterName() {
return 'JAVELIN';
}
public function getLintSeverityMap() {
return array(
self::LINT_MISSING_BINARY => ArcanistLintSeverity::SEVERITY_WARNING,
);
}
public function getLintNameMap() {
return array(
self::LINT_PRIVATE_ACCESS => 'Private Method/Member Access',
self::LINT_MISSING_DEPENDENCY => 'Missing Javelin Dependency',
self::LINT_UNNECESSARY_DEPENDENCY => 'Unnecessary Javelin Dependency',
self::LINT_UNKNOWN_DEPENDENCY => 'Unknown Javelin Dependency',
self::LINT_MISSING_BINARY => '`javelinsymbols` Not In Path',
);
}
private function shouldIgnorePath($path) {
return preg_match('@/__tests__/|externals/javelinjs/src/docs/@', $path);
}
public function lintPath($path) {
if ($this->shouldIgnorePath($path)) {
return;
}
if (!$this->haveSymbolsBinary) {
if (!$this->haveWarnedAboutBinary) {
$this->haveWarnedAboutBinary = true;
// TODO: Write build documentation for the Javelin binaries and point
// the user at it.
$this->raiseLintAtLine(
1,
0,
self::LINT_MISSING_BINARY,
"The 'javelinsymbols' binary in the Javelin project is not ".
"available in \$PATH, so the Javelin linter can't run. This ".
"isn't a big concern, but means some Javelin problems can't be ".
"automatically detected.");
}
return;
}
list($uses, $installs) = $this->getUsedAndInstalledSymbolsForPath($path);
foreach ($uses as $symbol => $line) {
$parts = explode('.', $symbol);
foreach ($parts as $part) {
if ($part[0] == '_' && $part[1] != '_') {
$base = implode('.', array_slice($parts, 0, 2));
if (!array_key_exists($base, $installs)) {
$this->raiseLintAtLine(
$line,
0,
self::LINT_PRIVATE_ACCESS,
"This file accesses private symbol '{$symbol}' across file ".
"boundaries. You may only access private members and methods ".
"from the file where they are defined.");
}
break;
}
}
}
if ($this->getEngine()->getCommitHookMode()) {
// Don't do the dependency checks in commit-hook mode because we won't
// have an available working copy.
return;
}
$external_classes = array();
foreach ($uses as $symbol => $line) {
$parts = explode('.', $symbol);
$class = implode('.', array_slice($parts, 0, 2));
if (!array_key_exists($class, $external_classes) &&
!array_key_exists($class, $installs)) {
$external_classes[$class] = $line;
}
}
$celerity = CelerityResourceMap::getInstance();
$path = preg_replace(
'@^externals/javelinjs/src/@',
'webroot/rsrc/js/javelin/',
$path);
$need = $external_classes;
$info = $celerity->lookupFileInformation(substr($path, strlen('webroot')));
if (!$info) {
$info = array();
}
$requires = idx($info, 'requires', array());
foreach ($requires as $key => $name) {
$symbol_info = $celerity->lookupSymbolInformation($name);
if (!$symbol_info) {
$this->raiseLintAtLine(
0,
0,
self::LINT_UNKNOWN_DEPENDENCY,
"This file @requires component '{$name}', but it does not ".
"exist. You may need to rebuild the Celerity map.");
unset($requires[$key]);
continue;
}
$symbol_path = 'webroot'.$symbol_info['disk'];
list($ignored, $req_install) = $this->getUsedAndInstalledSymbolsForPath(
$symbol_path);
if (array_intersect_key($req_install, $external_classes)) {
$need = array_diff_key($need, $req_install);
unset($requires[$key]);
}
}
foreach ($need as $class => $line) {
$this->raiseLintAtLine(
$line,
0,
self::LINT_MISSING_DEPENDENCY,
"This file uses '{$class}' but does not @requires the component ".
"which installs it. You may need to rebuild the Celerity map.");
}
foreach ($requires as $component) {
$this->raiseLintAtLine(
0,
0,
self::LINT_UNNECESSARY_DEPENDENCY,
"This file @requires component '{$component}' but does not use ".
"anything it provides.");
}
}
private function loadSymbols($path) {
if (empty($this->symbols[$path])) {
$this->symbols[$path] = $this->newSymbolsFuture($path)->resolvex();
}
return $this->symbols[$path];
}
private function newSymbolsFuture($path) {
- $javelinsymbols = 'javelinsymbols';
-
- $future = new ExecFuture($javelinsymbols.' # '.escapeshellarg($path));
+ $future = new ExecFuture('javelinsymbols # %s', $path);
$future->write($this->getData($path));
return $future;
}
private function getUsedAndInstalledSymbolsForPath($path) {
list($symbols) = $this->loadSymbols($path);
$symbols = trim($symbols);
$uses = array();
$installs = array();
if (empty($symbols)) {
// This file has no symbols.
return array($uses, $installs);
}
$symbols = explode("\n", trim($symbols));
foreach ($symbols as $line) {
$matches = null;
if (!preg_match('/^([?+\*])([^:]*):(\d+)$/', $line, $matches)) {
throw new Exception(
"Received malformed output from `javelinsymbols`.");
}
$type = $matches[1];
$symbol = $matches[2];
$line = $matches[3];
switch ($type) {
case '?':
$uses[$symbol] = $line;
break;
case '+':
$installs['JX.'.$symbol] = $line;
break;
}
}
$contents = $this->getData($path);
$matches = null;
$count = preg_match_all(
'/@javelin-installs\W+(\S+)/',
$contents,
$matches,
PREG_PATTERN_ORDER);
if ($count) {
foreach ($matches[1] as $symbol) {
$installs[$symbol] = 0;
}
}
return array($uses, $installs);
}
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sun, Jan 19, 20:00 (1 w, 4 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1128236
Default Alt Text
(30 KB)
Attached To
Mode
rP Phorge
Attached
Detach File
Event Timeline
Log In to Comment