1

we have a cakePHP Project and use a Path-class for dynamic linkcreations.

Example:

$this->redirect(Path::action_my_controller_path($id));

Would redirect to the action action of the MyController-class and handle the parameter $id.

So now my IDE (PHPStorm) is always complaining about the missing functions in Path-class as there is no specified action_my_controller_path($id)-function.

Does anyone know about an PHP-Doc annotation to suppress this messages?

edit: After "speaking" with LazyOne via comments I've added the following PHPDoc in the Path-class, as described in his link ( phpDocumentor ):

 /**
 *
 * @method array|string callStatic(string $name, string $args)
 */

class Path extends Object 

But I still get the Warning when calling my Path-class (see example above). The autogenerated phpDoc (from my IDE) for this method is is

/**
 * @param $name
 * @param $args
 *
 * @return array|string
 */
public static function __callStatic($name, $args){

If I write

 /**
 *
 * @method array|string __callStatic(string $name, string $args)
 */

class Path extends Object 

I get an Method with same name already defiend in class-error :/

edit2: Full Path-class

<?php

/**
 * Zentrale Klasse fuer die Pfade, wobei jede Methode eines der akzeptierten
 * Formate von Router#url zurueckgibt, damit Router#url diese dann in eine
 * URL (als String) umwandeln kann.
 *
 * h3. Benutzung im View:
 *
 * <pre>
 *   $url = Path::alliances_path();
 *   $Html->link($url);
 * </pre>
 *
 *
 * h3. Konventionen (ueber __callStatic umgesetzt):
 *
 * <pre>
 * Semantische URL          <- Methoden Aufruf
 * -----------------------------------------------------------
 * /alliances               <- Path::alliances_path()
 * /alliances/add           <- Path::new_alliance_path()
 * /alliances/view/:id      <- Path::show_alliance_path($id)
 * /alliances/edit/:id      <- Path::edit_alliance_path($id)
 * /alliances/delete/:id    <- Path::delete_alliance_path($id)
 * </pre>
 *
 * h4. Allgemein:
 *
 * Wenn wir direkt eine Entitaet (ueber eine id gegeben) ansprechen, so
 * muss der controller im Singular sein.
 *
 * <pre>
 * /:controller/:action/:id <- Path::`:action:`_`:controller:`_path($id)
 * `:controller:` muss im Singular sein
 * </pre>
 *
 * Wenn wir ueber alle Entitaeten reden bzw. keine spezifische Entitaet
 * ansprechen, dann muss der controller im Plural stehen.
 *
 * z.B. die Uebersicht von `alliances`
 * <pre>
 * /alliances               <- Path::alliances_path($id)
 * </pre>
 *
 * z.B. Custom Actions, die noch keine Entitaet kennen
 * <pre>
 * /accounts/register       <- Path::register_accounts_path()
 * </pre>
 *
 * z.B. Custom Actions mit ohne spez. Entitaet und named parameters
 * <pre>
 * /accounts/register/confirmation:done   <- Path::register_accounts_path(array('confirmation' => 'done'))
 * </pre>
 *
 * h4. Custom Actions:
 *
 * z.B. `kick` Action in AlliancesController
 * <pre>
 * /alliances/kick/:id      <- Path::kick_alliance_path($id)
 * </pre>
 *
 * oder
 *
 * z.B. `leave` in AlliancesController
 * <pre>
 * /alliances/leave/:id     <- Path::leave_alliance_path($id)
 * </pre>
 *
 * h4. Named Parameters:
 *
 * <pre>
 * /alliances/sort:user_id/order:asc  <- Path::alliances_path(array('sort' => 'user_id', 'order' => 'asc'))
 * /alliances/kick/:id/user:600001    <- Path::kick_alliance_path($id, array('user' => 600001))
 * </pre>
 *
 * @see http://book.cakephp.org/2.0/en/development/routing.html#named-parameters
 */
class Path extends Object {

  /** @const */
  const LOGIN_PATH  = '/auth/login';
  const LOGOUT_PATH = '/auth/logout';
  const WELCOME_PATH = '/welcome';

  public static $settings = array(
    'signup' => array(
        'singular' => true,
        'plugin' => false,
        'entityActions' => array('disabled', 'world', 'code_expired', 'confirmation', 'disallowed', 'resend') # must be underscored
      ),
    'recover' => array(
        'singular' => true,
        'plugin' => false,
        'entityActions' => array('account', 'verify', 'token_expired')
      ),
    'support' => array(
        'singular' => true,
        'plugin' => false,
        'entityActions' => array('banned')
      ),
    'contact' => array(
        'singular' => true,
        'plugin' => false
      ),
    'about' => array(
        'singular' => true,
        'plugin' => false
      ),
    'auth' => array(
        'singular' => true,
        'plugin' => false,
        'entityActions' => array('counselor')
      ));



  public static function parseUrl($url) {
    $url = Router::normalize($url);
    $request = new CakeRequest($url, false);

    $params = Router::parse($request->url);
    $request->addParams($params);

    return $request;
  }

  protected static function getId($id){
    // ist kein array, muss id sein
    if(!is_array($id)) {
      return $id;
    }

    // id column vorhanden! als id nehmen
    if(isset($id['id'])) {
      return $id['id'];
    }

    trigger_error('Path: missing id column', E_USER_NOTICE);
    return null;
  }

  /* extract first argument to support:
   * - edit_user_path(1);
   * - edit_user_path(array(1));
   * - edit_user_path(array('id' => 1));
   * url will be /users/edit/1
   *
   * to suport named parameters use second array
   * - edit_user_path(1, array('key' => 'value'))
   * url will result as /users/edit/1/key:value
   */
  /**
   * @param      $args
   * @param bool $requires_id
   *
   * @return array
   */
  protected static function extractOptions($args, $requires_id = false){

    // keine argumente vorhanden
    if(!isset($args[0])) {
      return array();
    }

    // setzen von named parameters
    if($requires_id) {
      $named_parameters = isset($args[1]) && is_array($args[1]) ? $args[1] : array();
    } else {
      $named_parameters = is_array($args[0]) ? $args[0] : array();
    }

    // /alliances/index/name:parameter
    // -> hat keinen index, darum nur die
    // named_paramerters zurueck geben
    if(!$requires_id) {
      return $named_parameters;
    }

    // id muss vorhanden sein!, da kein index
    $id = self::getId($args[0]);
    return array_merge($named_parameters, array($id));
  }

  /**
   * @param $name
   * @param $args
   *
   * @return array|string
   */
  public static function __callStatic($name, $args){
    $parts = explode('_', $name);
    $path_or_url = array_pop($parts);

    # convert path into url
    if($path_or_url === 'url') {
      $name = implode('_', $parts) . '_path';

      # custom paths have priority over general
      $url  = call_user_func_array('static::' . $name, $args);
      return h(Router::url($url, true));
    }

    # neither path nor url
    if($path_or_url !== 'path') {
      return parent::__callStatic($name, $args);
    }

    if(count($parts) == 1) {
      # the short syntax
      $action = 'view';
      $controller_raw = $parts[0];
    } else {
      # first part is always the action
      $action = array_shift($parts);
      # for Path::action_complex_controller_path syntax we have to join
      $controller_raw = implode('_', $parts);
    }

    # underscore controller and action
    $action         = Inflector::underscore($action);
    $controller_raw = Inflector::underscore($controller_raw);

    $settings = isset(static::$settings[$controller_raw]) ?
                      static::$settings[$controller_raw] : array();

    # alias for more descriptive actions
    if($action === 'show') {
      $action = 'view';
    }else if($action === 'new') {
      $action = 'add';
    }

    # if the controller is in singular
    if(isset($settings['singular']) && $settings['singular']) {
      $controller = $controller_raw;
      $no_entity_path = true;
    } else {
      $controller = Inflector::pluralize($controller_raw);
      $no_entity_path = $controller == $controller_raw;
    }

    $is_index_path = $action == 'view' && $no_entity_path;

    # if the controller is in plural, then it is no entitiy
    $is_entity_path = (!$no_entity_path && $action != 'add') || in_array($action, (array)$settings['entityActions']);

    $defaults = array(
      'controller' => $controller,
      'action' => $is_index_path ? 'index' : $action,
      'plugin' => (boolean)$settings['plugin']
    );

    $options = static::extractOptions($args, $is_entity_path);

    return array_merge($defaults, $options);
  }
}
11
  • Have you tried Alt+Enter on the error? In any case: you can lower the severity for that inspection (it has several options) .. or you can even disable that inspection altogether (if so desired). Commented Aug 21, 2014 at 16:09
  • Alt+Enter adds the missing function, but I don't need this, as the Path-class generates the link via regEx. I also don't want to disable or lower the severity for the missing function inspection as I only want to supress it on the Path-class but not on other classes. Thanks anyway so far LazyOne Commented Aug 21, 2014 at 17:52
  • 1) Show screenshot of your Alt+Enter menu -- you seems may not know what other functionality it has/can do; 2) You can lower severity for cases where such class has magic __call/__callStatic methods -- just your case. Have you checked what options that Inspection has .. or this was just "I have not checked but I think..." moment? Commented Aug 21, 2014 at 17:56
  • 1) I think you target the "suppress this statement"-option? That's almost what I need but I'd like to have a "global" suppression I could define in the Path-class. If this doesn't work I have to spam my code with the "suppress this statement"-option :/ 2) I know that I can lower severity for the magic call methods. But I like to get a warning when a method is not found - except the Path-class. Commented Aug 21, 2014 at 18:11
  • Excellent -- it's good that you are aware of all those moments (now?). If you were about them before -- you should have mentioned them straight away so I would not had to ask these questions/write them in such manner. In any case: This Path class of yours -- is this actually yours (I mean -- you can freely modify it) or it is part of some framework which you cannot touch? Commented Aug 21, 2014 at 18:30

1 Answer 1

2

If you want PhpStorm to stop complaining about accessing action_my_controller_path() method (or any other SPECIFIC method) -- then use @method to declare it:

BEFORE:

enter image description here

AFTER:

enter image description here

You do not have to declare every single method -- only those that you are planning to call DIRECTLY, like you did here: $this->redirect(Path::action_my_controller_path($id));


If you want PhpStorm to stop complaining about accessing ANY non-existing method, then you cannot do anything about it -- because you have rejected to use all currently available options for such case:

  • Lowering severity for accessing non-existing methods when magic __call/__callStatic are available .. or completely disable that inspection (last one could be an overkill, I agree);
  • "Suppress for statement" option to suppress warning in that particular place.

Unfortunately there is no @method syntax available where you can use wildcards, e.g. @method static array *(int $id);.

Sign up to request clarification or add additional context in comments.

1 Comment

If all of your text is emphasised, nothing is.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.