465 lines
15 KiB
PHP
465 lines
15 KiB
PHP
<?php
|
|
|
|
require_once('DojoFunctionBody.php');
|
|
require_once('DojoBlock.php');
|
|
require_once('DojoParameters.php');
|
|
|
|
class DojoFunctionDeclare extends DojoBlock
|
|
{
|
|
private $object = 'DojoFunctionDeclare';
|
|
|
|
private $parameters;
|
|
private $function_name;
|
|
protected $body;
|
|
|
|
private $in_executed_function = null;
|
|
private $anonymous = false;
|
|
private $prototype = '';
|
|
private $constructor = false;
|
|
private $aliases = '';
|
|
|
|
public function __construct($package, $line_number = false, $position = false){
|
|
parent::__construct($package, $line_number, $position);
|
|
$this->parameters = new DojoParameters($package);
|
|
$this->body = new DojoFunctionBody($package);
|
|
}
|
|
|
|
public static function parseVariable(&$comment) {
|
|
$summary = $comment;
|
|
$tags = array();
|
|
if (preg_match('%^\s*([a-z\s]+)\]\s*%', $summary, $match)) {
|
|
$tags = preg_split('%\s+%', $match[1]);
|
|
$summary = $comment = substr($summary, strlen($match[0]));
|
|
}
|
|
|
|
list($type, $summary) = preg_split('%\s%', $summary, 2);
|
|
$type = preg_replace('%(^[^a-zA-Z0-9._$]|[^a-zA-Z0-9._$?]$)%', '', $type);
|
|
|
|
$options = array();
|
|
if(!empty($type)){
|
|
if(strpos($type, '?')){
|
|
$type = substr($type, 0, strlen($type) - 1);
|
|
$options['optional'] = true;
|
|
}
|
|
if(strpos($type, '...')){
|
|
$type = substr($type, 0, strlen($type) - 3);
|
|
$options['repeating'] = true;
|
|
}
|
|
}
|
|
|
|
return array($tags, $type, $options, $summary);
|
|
}
|
|
|
|
public function destroy() {
|
|
if (!$this->destroyed) {
|
|
$this->destroyed = true;
|
|
$this->parameters->destroy();
|
|
unset($this->parameters);
|
|
$this->body->destroy();
|
|
unset($this->body);
|
|
if ($this->in_executed_function) {
|
|
$this->in_executed_function->destroy();
|
|
}
|
|
unset($this->in_executed_function);
|
|
}
|
|
}
|
|
|
|
public function getFunctionName(){
|
|
return $this->function_name;
|
|
}
|
|
|
|
public function getAliases(){
|
|
return $this->aliases;
|
|
}
|
|
|
|
public function rebuildAliases($map) {
|
|
if (is_array($this->aliases)) {
|
|
foreach ($this->aliases as $i => $alias) {
|
|
foreach ($map as $internal_name => $external_name) {
|
|
if (strpos($alias, $internal_name . '.') === 0) {
|
|
if (!$external_name) continue 2;
|
|
$alias = $external_name . substr($alias, strlen($internal_name));
|
|
}
|
|
}
|
|
$this->aliases[$i] = $alias;
|
|
}
|
|
}
|
|
}
|
|
|
|
public function setFunctionName($function_name){
|
|
$this->function_name = $function_name;
|
|
}
|
|
|
|
public function setPrototype($function_name){
|
|
$this->prototype = $function_name;
|
|
}
|
|
|
|
public function getPrototype(){
|
|
return $this->prototype;
|
|
}
|
|
|
|
public function setInstance($function_name){
|
|
$this->instance = $function_name;
|
|
}
|
|
|
|
public function getInstance(){
|
|
return $this->instance;
|
|
}
|
|
|
|
public function setConstructor($constructor){
|
|
$this->constructor = $constructor;
|
|
}
|
|
|
|
public function isConstructor(){
|
|
return $this->constructor;
|
|
}
|
|
|
|
public function setAnonymous($anonymous) {
|
|
$this->anonymous = $anonymous;
|
|
}
|
|
|
|
public function isAnonymous(){
|
|
return $this->anonymous;
|
|
}
|
|
|
|
public function isThis(){
|
|
return ($this->prototype || $this->instance);
|
|
}
|
|
|
|
public function getThis(){
|
|
return ($this->prototype) ? $this->prototype : $this->instance;
|
|
}
|
|
|
|
public function setExecutedFunction($function) {
|
|
$this->in_executed_function = $function;
|
|
}
|
|
|
|
public function getInstanceVariableNames(){
|
|
return array_unique($this->body->getInstanceVariableNames());
|
|
}
|
|
|
|
public function removeSwallowedMixins(&$mixins) {
|
|
return $this->body->removeSwallowedMixins($mixins);
|
|
}
|
|
|
|
public function getReturnComments(){
|
|
return array_unique($this->body->getReturnComments());
|
|
}
|
|
|
|
public function getThisInheritanceCalls(){
|
|
$output = array();
|
|
$calls = array_unique($this->body->getThisInheritanceCalls());
|
|
|
|
if ($this->in_executed_function) {
|
|
$internalized = $this->in_executed_function->getLocalVariableNames();
|
|
}
|
|
|
|
$parameters = $this->getParameterNames();
|
|
foreach ($calls as $call) {
|
|
if (!in_array($call, $parameters)) {
|
|
if ($internalized) {
|
|
foreach (array_keys($internalized) as $variable) {
|
|
if (strpos($call, $variable . '.') === 0) {
|
|
continue 2;
|
|
}
|
|
}
|
|
}
|
|
$output[] = $call;
|
|
}
|
|
}
|
|
|
|
return $output;
|
|
}
|
|
|
|
public function getVariableNames($function_name, $parameter_names=array()){
|
|
return $this->body->getExternalizedVariableNames($function_name, $parameter_names);
|
|
}
|
|
|
|
public function getFunctionDeclarations(){
|
|
return $this->body->getExternalizedFunctionDeclarations();
|
|
}
|
|
|
|
public function getObjects(){
|
|
return $this->body->getExternalizedObjects(false, $this->getParameterNames());
|
|
}
|
|
|
|
public function getLocalVariableNames(){
|
|
return $this->body->getLocalVariableNames();
|
|
}
|
|
|
|
public function removeCodeFrom($lines){
|
|
$this->build();
|
|
|
|
return Text::blankOutAtPositions($lines, $this->start[0], $this->start[1], $this->end[0], $this->end[1]);
|
|
}
|
|
|
|
public function build(){
|
|
if (!$this->start) {
|
|
die("DojoFunctionDeclare->build() used before setting a start position");
|
|
}
|
|
if($this->end){
|
|
return $this->end;
|
|
}
|
|
|
|
$lines = Text::chop($this->package->getCode(), $this->start[0], $this->start[1]);
|
|
$line = trim($lines[$this->start[0]]);
|
|
if(strpos($line, 'function') === 0){
|
|
$line = substr($line, 8);
|
|
preg_match('%[^\s]%', $line, $match);
|
|
if($match[0] != '('){
|
|
$this->function_name = trim(substr($line, 0, strpos($line, '(')));
|
|
}
|
|
}else{
|
|
$name = trim(substr($line, 0, strpos($line, '=')));
|
|
$extra = substr($line, strpos($line, '=') + 1);
|
|
if(preg_match('%^\s+new\s+%', $name, $match) || preg_match('%^\s*new\s+%', $extra, $match)){
|
|
$this->anonymous = true;
|
|
$name = str_replace($match[0], '', $name);
|
|
}
|
|
if(($pos = strpos($name, '.prototype.')) !== false){
|
|
$this->prototype = substr($name, 0, $pos);
|
|
$name = str_replace('.prototype', '', $name);
|
|
}
|
|
if(($pos = strpos($name, 'this.')) === 0){
|
|
$this->instance = $this->getFunctionName();
|
|
$name = $this->getFunctionName() . "." . preg_replace('%^this\.%', '', $name);
|
|
}
|
|
|
|
if (!$this->isAnonymous()) {
|
|
$full_lines = Text::chop($this->package->getCode(), $this->start[0], 0);
|
|
$full_line = substr($full_lines[$this->start[0]], 0, $this->start[1]);
|
|
if (preg_match('%(?:[a-zA-Z0-9._$]+\s*=\s*)+$%', $full_line, $matches)) {
|
|
$aliases = preg_split('%\s*=\s*%', $matches[0]);
|
|
foreach ($aliases as $alias) {
|
|
$alias = trim($alias);
|
|
if ($alias) {
|
|
if (strpos($alias, 'this.') === 0) {
|
|
$alias = $this->getFunctionName() . "." . preg_replace('%^this\.%', '', $alias);
|
|
}
|
|
$this->aliases[] = $alias;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (strpos($name, '[') !== false) {
|
|
$source_lines = Text::chop($this->package->getSource(), $this->start[0], $this->start[1]);
|
|
$source_line = trim($source_lines[$this->start[0]]);
|
|
preg_match('%^\s*([a-zA-Z_.$][\w.$]*(?:\.[a-zA-Z_.$][\w.$]|\["[^"]+"\])*)\s*=\s*function%', $source_line, $match);
|
|
$name = preg_replace('%\["([^"]+)"\]%', '.$1', $match[1]);
|
|
}
|
|
$this->function_name = $name;
|
|
}
|
|
|
|
$this->parameters->setStart($this->start[0], strpos($lines[$this->start[0]], '('));
|
|
$end = $this->parameters->build();
|
|
|
|
$lines = Text::chop($this->package->getCode(), $end[0], $end[1]);
|
|
foreach($lines as $line_number => $line){
|
|
if(($pos = strpos($line, '{')) !== false){
|
|
$this->body->setStart($line_number, $pos);
|
|
return $this->end = $this->body->build();
|
|
}
|
|
}
|
|
}
|
|
|
|
public function getParameter($pos){
|
|
return $this->parameters->getParameter($pos);
|
|
}
|
|
|
|
public function getParameters(){
|
|
return $this->parameters->getParameters();
|
|
}
|
|
|
|
public function getParameterNames(){
|
|
$names = array();
|
|
$parameters = $this->getParameters();
|
|
foreach ($parameters as $parameter) {
|
|
if($parameter->isA(DojoVariable)){
|
|
$names[] = $parameter->getVariable();
|
|
}
|
|
}
|
|
return $names;
|
|
}
|
|
|
|
public function addBlockCommentKey($key){
|
|
$this->body->addBlockCommentKey($key);
|
|
}
|
|
|
|
public function addBlockCommentKeySet($key){
|
|
$this->body->addBlockCommentKeySet($key);
|
|
}
|
|
|
|
public function getBlockCommentKeys(){
|
|
return $this->body->getBlockCommentKeys();
|
|
}
|
|
|
|
public function getBlockComment($key){
|
|
return $this->body->getBlockComment($key);
|
|
}
|
|
|
|
public function getSource(){
|
|
return $this->body->getSource();
|
|
}
|
|
|
|
public function getInstanceFunctions($function_name){
|
|
return $this->body->getInstanceFunctions($function_name);
|
|
}
|
|
|
|
public function rollOut(&$output){
|
|
// Basically, any this.variables in here never impact this object, they apply to the "this" function
|
|
$masquerading_as_function = $function_name = $this->getFunctionName();
|
|
if (substr($masquerading_as_function, 0, 7) == 'window.'){
|
|
$masquerading_as_function = $function_name = substr($masquerading_as_function, 7);
|
|
}
|
|
$check_keys = array('summary','description','returns','tags','exceptions');
|
|
|
|
if (!empty($output[$function_name]['aliases'])) {
|
|
unset($output[$function_name]['aliases']); // This is implemented, it aliases nothing.
|
|
}
|
|
|
|
if ($this->isThis()) {
|
|
$masquerading_as_function = $this->getThis();
|
|
}
|
|
|
|
$output[$function_name]['type'] = 'Function';
|
|
if (!empty($output[$masquerading_as_function])) {
|
|
$output[$masquerading_as_function]['type'] = 'Function';
|
|
}
|
|
|
|
if ($aliases = $this->getAliases()) {
|
|
foreach ($aliases as $alias) {
|
|
if (empty($output[$alias])) {
|
|
$output[$alias]['aliases'] = $function_name;
|
|
}
|
|
}
|
|
}
|
|
|
|
$parameters = $this->getParameters();
|
|
foreach ($parameters as $parameter) {
|
|
if($parameter->isA(DojoVariable)){
|
|
$parameter_name = $parameter->getVariable();
|
|
$parameter_type = $parameter->getType();
|
|
if(strpos($parameter_type, '?')){
|
|
$parameter_type = substr($parameter_type, 0, strlen($parameter_type) - 1);
|
|
$output[$function_name]['parameters'][$parameter_name]['optional'] = true;
|
|
}
|
|
if(strpos($parameter_type, '...')){
|
|
$parameter_type = substr($parameter_type, 0, strlen($parameter_type) - 3);
|
|
$output[$function_name]['parameters'][$parameter_name]['repeating'] = true;
|
|
}
|
|
if (empty($output[$function_name]['parameters'][$parameter_name]['type']) || $parameter_type) {
|
|
$output[$function_name]['parameters'][$parameter_name]['type'] = $parameter_type;
|
|
}
|
|
|
|
$this->addBlockCommentKey($parameter->getVariable());
|
|
}
|
|
}
|
|
|
|
if ($this->isAnonymous()) {
|
|
$output[$function_name]['initialized'] = true;
|
|
|
|
$declarations = $this->body->getExternalizedFunctionDeclarations($function_name);
|
|
foreach ($declarations as $declaration) {
|
|
$declaration->rollout($output);
|
|
}
|
|
|
|
$variables = $this->body->getExternalizedInstanceVariableNames($function_name, $this->getParameterNames());
|
|
foreach($variables as $variable) {
|
|
$output[$function_name . '.' . $variable]['instance'] = $function_name;
|
|
}
|
|
|
|
$variables = $this->body->getExternalizedVariableNames($function_name, $this->getParameterNames());
|
|
foreach($variables as $variable) {
|
|
list($first,) = explode('.', $variable, 2);
|
|
if (!is_array($output[$function_name]['parameters']) || !array_key_exists($first, $output[$function_name]['parameters'])) {
|
|
if (empty($output[$variable])) {
|
|
$output[$variable] = array();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach($check_keys as $ck){
|
|
$this->addBlockCommentKey($ck);
|
|
}
|
|
$this->addBlockCommentKeySet('example');
|
|
$check_keys[] = 'example';
|
|
|
|
$output[$function_name]['source'] = $this->getSource();
|
|
|
|
$all_variables = array();
|
|
$instance_variables = $this->getInstanceVariableNames();
|
|
foreach($instance_variables as $instance_variable){
|
|
$this->addBlockCommentKey($instance_variable);
|
|
$all_variables[] = $instance_variable;
|
|
|
|
$full_variable_name = "{$masquerading_as_function}.{$instance_variable}";
|
|
$output[$full_variable_name]['instance'] = $masquerading_as_function;
|
|
}
|
|
|
|
$instance_functions = $this->getInstanceFunctions($function_name);
|
|
foreach($instance_functions as $instance_function){
|
|
$instance_function->rollOut($output);
|
|
$output[$instance_function->getFunctionName()]['instance'] = $function_name;
|
|
}
|
|
|
|
$comment_keys = $this->getBlockCommentKeys();
|
|
foreach($comment_keys as $key){
|
|
if ($key == 'returns') {
|
|
$output[$function_name]['return_summary'] = $this->getBlockComment($key);
|
|
}
|
|
elseif (in_array($key, $check_keys)) {
|
|
$output[$function_name][$key] = $this->getBlockComment($key);
|
|
}
|
|
if (in_array($key, $all_variables) && $comment = $this->getBlockComment($key)) {
|
|
list($type, $comment) = preg_split('%\s+%', $comment, 2);
|
|
$type = preg_replace('%(^[^a-zA-Z0-9._$]|[^a-zA-Z0-9._$?]$)%', '', $type);
|
|
if($type){
|
|
$output[$function_name . '.' . $key]['type'] = $type;
|
|
}
|
|
$output[$function_name . '.' . $key]['summary'] = $comment;
|
|
}
|
|
if (!empty($output[$function_name]['parameters']) && array_key_exists($key, $output[$function_name]['parameters']) && $comment = $this->getBlockComment($key)) {
|
|
list($tags, $parameter_type, $options, $summary) = DojoFunctionDeclare::parseVariable($comment);
|
|
|
|
// If type is specified in the parameters, and it doesn't
|
|
// match the first word in this comment block, assume that
|
|
// this first word doesn't represent its type
|
|
if (!empty($output[$function_name]['parameters'][$key]['type']) && $parameter_type != $output[$function_name]['parameters'][$key]['type']) {
|
|
$summary = $comment;
|
|
$parameter_type = $output[$function_name]['parameters'][$key]['type'];
|
|
}
|
|
$output[$function_name]['parameters'][$key] = array_merge($output[$function_name]['parameters'][$key], $options);
|
|
$output[$function_name]['parameters'][$key]['type'] = $parameter_type;
|
|
$output[$function_name]['parameters'][$key]['summary'] = htmlentities($summary);
|
|
}
|
|
}
|
|
|
|
$returns = $this->getReturnComments();
|
|
if (count($returns)){
|
|
$output[$function_name]['returns'] = implode('|', $returns);
|
|
}
|
|
|
|
if ($output[$function_name]['example']) {
|
|
$output[$function_name]['examples'] = $output[$function_name]['example'];
|
|
unset($output[$function_name]['example']);
|
|
}
|
|
|
|
if($calls = $this->getThisInheritanceCalls()){
|
|
foreach ($calls as $call) {
|
|
$output[$function_name]['chains']['call'][] = $call;
|
|
}
|
|
}
|
|
|
|
if($this->getPrototype()){
|
|
$output[$function_name]['prototype'] = $this->getPrototype();
|
|
}
|
|
if($this->getInstance()){
|
|
$output[$function_name]['instance'] = $this->getInstance();
|
|
}
|
|
}
|
|
}
|
|
|
|
?>
|