/** * @param Database $database * The database to read from * * @param ModelOne $model * The source model of the association * * @return Model * Read a model from the database with the given pk */ public function read(Database $database, ModelOne $model) { $read_closure = $this->read_closure; // Select all rows for this PK from the // association table $select_query = $this->schema->queryForSelectColumnValue('source_pk', $model->primaryKeyValue()); $result = $database->query($select_query); if (!$result) { $read_closure($model, []); return; } $column = []; while ($row = $result->fetchArray(SQLITE3_ASSOC)) { $column[] = $row['value']; } // Write the map to the model $read_closure($model, $column); }
public function store() { if (!Database::isEnabled()) { return; } $this->storeClassMap(); $this->storeMethodMap(); $this->storeConstantMap(); $this->storePropertyMap(); $this->storeFileMap(); }
/** * Analyze the parameters and arguments for a call * to the given method or function * * @param CodeBase $code_base * @param Method $method * @param Node $node * * @return null */ private function analyzeCallToMethod(CodeBase $code_base, Method $method, Node $node) { if (Database::isEnabled()) { // Store the call to the method so we can track // dependencies later (new CalledBy((string) $method->getFQSEN(), $this->context))->write(Database::get()); } // Create variables for any pass-by-reference // parameters $argument_list = $node->children['args']; foreach ($argument_list->children as $i => $argument) { $parameter = $method->getParameterList()[$i] ?? null; if (!$parameter) { continue; } // If pass-by-reference, make sure the variable exists // or create it if it doesn't. if ($parameter->isPassByReference()) { if ($argument->kind == \ast\AST_VAR) { // We don't do anything with it; just create it // if it doesn't exist $variable = AST::getOrCreateVariableFromNodeInContext($argument, $this->context, $this->code_base); } else { if ($argument->kind == \ast\AST_STATIC_PROP || $argument->kind == \ast\AST_PROP) { $property_name = $argument->children['prop']; if (is_string($property_name)) { // We don't do anything with it; just create it // if it doesn't exist try { $property = AST::getOrCreatePropertyFromNodeInContext($argument->children['prop'], $argument, $this->context, $this->code_base); } catch (CodeBaseException $exception) { Log::err(Log::EUNDEF, $exception->getMessage(), $this->context->getFile(), $node->lineno); } catch (NodeException $exception) { // If we can't figure out what kind of a call // this is, don't worry about it } } else { // This is stuff like `Class->$foo`. I'm ignoring // it. } } } } } // Confirm the argument types are clean ArgumentType::analyze($method, $node, $this->context, $this->code_base); // Take another pass over pass-by-reference parameters // and assign types to passed in variables foreach ($argument_list->children as $i => $argument) { $parameter = $method->getParameterList()[$i] ?? null; if (!$parameter) { continue; } // If the parameter is pass-by-reference and we're // passing a variable in, see if we should pass // the parameter and variable types to eachother $variable = null; if ($parameter->isPassByReference()) { if ($argument->kind == \ast\AST_VAR) { $variable = AST::getOrCreateVariableFromNodeInContext($argument, $this->context, $this->code_base); } else { if ($argument->kind == \ast\AST_STATIC_PROP || $argument->kind == \ast\AST_PROP) { $property_name = $argument->children['prop']; if (is_string($property_name)) { // We don't do anything with it; just create it // if it doesn't exist try { $variable = AST::getOrCreatePropertyFromNodeInContext($argument->children['prop'], $argument, $this->context, $this->code_base); } catch (CodeBaseException $exception) { Log::err(Log::EUNDEF, $exception->getMessage(), $this->context->getFile(), $node->lineno); } catch (NodeException $exception) { // If we can't figure out what kind of a call // this is, don't worry about it } } else { // This is stuff like `Class->$foo`. I'm ignoring // it. } } } if ($variable) { $variable->getUnionType()->addUnionType($parameter->getUnionType()); } } } // If we're in quick mode, don't retest methods based on // parameter types passed in if (Config::get()->quick_mode) { return; } // We're going to hunt to see if any of the arguments // have a mismatch with the parameters. If so, we'll // re-check the method to see how the parameters impact // its return type $has_argument_parameter_mismatch = false; // Now that we've made sure the arguments are sufficient // for definitions on the method, we iterate over the // arguments again and add their types to the parameter // types so we can test the method again $argument_list = $node->children['args']; // We create a copy of the parameter list so we can switch // back to it after $original_parameter_list = $method->getParameterList(); foreach ($argument_list->children as $i => $argument) { $parameter = $method->getParameterList()[$i] ?? null; if (!$parameter) { continue; } // If the parameter has no type, pass the // argument's type to it if ($parameter->getUnionType()->isEmpty()) { $has_argument_parameter_mismatch = true; $argument_type = UnionType::fromNode($this->context, $this->code_base, $argument); // If this isn't an internal function or method // and it has no type, add the argument's type // to it so we can compare it to subsequent // calls if (!$parameter->getContext()->isInternal()) { // Clone the parameter in the original // parameter list so we can reset it // later $original_parameter_list[$i] = clone $parameter; // Then set the new type on that parameter based // on the argument's type. We'll use this to // retest the method with the passed in types $parameter->getUnionType()->addUnionType($argument_type); } } } // Now that we know something about the parameters used // to call the method, we can reanalyze the method with // the types of the parameter, making sure we don't get // into an infinite loop of checking calls to the current // method in scope if ($has_argument_parameter_mismatch && !$method->getContext()->isInternal() && (!$this->context->isMethodScope() || $method->getFQSEN() !== $this->context->getMethodFQSEN())) { $method->analyze($method->getContext(), $code_base); } // Reset to the original parameter list after having // tested the parameters with the types passed in $method->setParameterList($original_parameter_list); }
/** * Mark the file at the given path as up to date so * that we know if its changed on subsequent runs * * @return null */ public function setParseUpToDate() { $this->modification_time = filemtime(realpath($this->file_path)); if (Database::isEnabled()) { // Write it to disk (new FileModel($this))->write(Database::get()); } }
/** * @return null */ protected function flushMethodWithScopeAndName(string $scope, string $name) { if (Database::isEnabled()) { MethodModel::delete(Database::get(), $scope . '|' . $name); } unset($this->method_map[$scope][$name]); }
/** * Save all file state * * @return null */ public function storeFileMap() { if (!Database::isEnabled()) { return; } foreach ($this->file_map as $file) { (new FileModel($file))->write(Database::get()); } }
/** * @return null */ protected function flushConstantWithScopeAndName(string $scope, string $name) { // Remove it from the database if (Database::isEnabled()) { ConstantModel::delete(Database::get(), $scope . '|' . $name); } // Remove it from memory unset($this->constant_map[$scope][$name]); }
/** * Initialize this table in the given database the first * time the method is called and never again. * * @return null */ public function initializeOnce(Database $database) { $this->memoize(__METHOD__, function () use($database) { $query = $this->queryForCreateTable(); // Make sure the table has been created $database->exec($query); // Execute each creation query to add additional // table constraints, etc. foreach ($this->create_query_list as $query) { $database->exec($query); } return 1; }); }
/** * @return FileRef[] * A list of references to this typed structural element. */ public function getReferenceList() : array { if (!empty($this->reference_list)) { return $this->reference_list; } // If we have a database, see if we have some callers // defined there and save those if (Database::isEnabled()) { $this->reference_list = array_map(function (CalledBy $called_by) : FileRef { return $called_by->getFileRef(); }, CalledBy::findManyByFQSEN(Database::get(), $this->getFQSEN())); } return $this->reference_list; }
/** * @return null */ protected function flushClassWithFQSEN(FullyQualifiedClassName $fqsen) { // Remove it from the database if (Database::isEnabled()) { ClazzModel::delete(Database::get(), (string) $fqsen); } // Remove it from memory unset($this->class_map[$fqsen]); }
/** * @param FileRef $file_ref * A reference to a location in which this typed structural * element is referenced. * * @return void */ public function addReference(FileRef $file_ref) { $this->reference_list[] = $file_ref; // If requested, save the reference to the // database if (Database::isEnabled()) { if ($this instanceof Addressable) { (new CalledBy((string) $this->getFQSEN(), $file_ref))->write(Database::get()); } } }